[SHOI2015]激光发生器,计算几何 直线相交
https://www.lydsy.com/JudgeOnline/problem.php?id=4595
算是比较明显的线段相交应用题,
涉及到:方向向量,直线相交,点在线段上的判断,直线的方向判断,向量求夹角,向量的旋转
感觉acm赛制下很容易错几次,但是只要写对了总能ac,
而oi赛制想直接拿到满分就很难很难了...
坑点的话,用atan2求向量夹角会出大事,基本过不去
#include<iostream>
#include<streambuf>
#include<string>
#include<cmath>
#include<math.h>
#include<vector>
#include<algorithm>
#include<map>
#include<deque>
#include<stdio.h>
#include<iomanip>
#include<queue>
#define ll long long
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define per(ii,a,b) for(int ii=b;ii>=a;--ii)
#define forn(i,x,g,e) for(int i=g[x];i;i=e[i].next)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define ull unsigned long long
#define fi first
#define se second
#define mp make_pair
#define db double
#define pb push_back
#define pii pair<db,int>
#define all(x) x.begin(),x.end()
#define show(x) cout<<#x<<"="<<x<<endl
#define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
#define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
#define show4(w,x,y,z) cout<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
#define show5(v,w,x,y,z) cout<<#v<<"="<<v<<" "<<#w<<"="<<w<<" "<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
#define showa(x,a,b) cout<<#x<<": ";rep(i,a,b) cout<<x[i]<<' ';cout<<endl
using namespace std;//head
const int maxn=4e5+10,maxm=2e6+10;
const ll INF=0x3f3f3f3f,mod=1e9+7;
int casn,m,k,n;
//@点类与基础@
const db eps = 1e-8;
const db pi = acos(-1);
const db inf = 1e9;
int sign(db x){ if(fabs(x) < eps) return 0; return x > 0 ? 1 : -1; }
//#define sign(x) x
int cmp(db k1, db k2){ return sign(k1-k2); }
//@k1在k2、k3之间@:
bool inmid(db k1, db k2, db k3){ return sign(k2-k1)*sign(k3-k1) <= 0; }
//@区间相交判定,区间1在区间2前@:
bool intersect(db l1,db r1,db l2,db r2){
if(l1>r1) swap(l1,r1); if(l2>r2) swap(l2,r2); return cmp(r1,l2)!=-1 && cmp(r2,l1)!=-1;
}
struct point{
db x, y;
point(){}
point(db k1, db k2){ x = k1, y = k2; }
//@向量加法、点+向量=点:@*/
point operator + (const point &k1) const { return point(x+k1.x, y+k1.y); }
//@向量减法、点-点=向量:@*/
point operator - (const point &k1) const { return point(x-k1.x, y-k1.y); }
//@向量数乘:@*/
point operator * (db k1) const { return (point){x*k1, y*k1}; }
//@向量数除:@*/
point operator / (db k1) const { return (point){x/k1, y/k1}; }
//@比较两个点(向量)是否相同:@*/
bool operator == (const point &k1) const { return cmp(x,k1.x)==0 && cmp(y,k1.y)==0; }
//@逆时针旋转:@*/
point turn(db k1){return (point){x*cos(k1)-y*sin(k1), x*sin(k1)+y*cos(k1)};}
//@逆时针旋转90度:@*/
point turn90(){return (point){-y, x};}
//@比较两个点(向量)的大小:@
//@x越小则点越小,若x相等,则y越小点越小.可以实现按点的坐标排序@*/
bool operator < (const point k1) const{
int a = cmp(x, k1.x);
if(a == -1) return 1; else if(a == 1) return 0; else return cmp(y,k1.y)==-1;
}
//@向量模长:@
db len(){ return sqrt(x*x+y*y); }
//@向量模长的平方:@
db len2(){ return x*x+y*y; }
//@单位向量:@
point unit(){ return (*this)/(*this).len(); }
//@向量的极角:@
db angle() { return atan2(y, x); }
//@将点放入第一象限:@
//@当横坐标为负时,或横坐标为0纵坐标为负时,将点按原点做对称 角度是$[-π/2, π/2]$@
point getdel(){
if (sign(x)==-1||(sign(x)==0&&sign(y)==-1)) return (*this)*(-1);
else return (*this);
}
//@判断点是否在1 2象限,或者在x的负半轴上 角度是(0, π]@
bool getp() const {return sign(y)==1 || (sign(y)==0&&sign(x)==-1); }
void scan(){cin>>x>>y;}
void print(){cout<<x<<' '<<y<<'\n'; }
};
//@判断k1 在 [k2,k3] 内:@
bool inmid(point k1, point k2, point k3){
return inmid(k1.x,k2.x,k3.x) && inmid(k1.y,k2.y,k3.y);
}
//@得到两点中点:@
point midpo(point k1, point k2){ return (k1+k2)/2; }
//@两点距离的平方@
db dis2(point k1, point k2){ return (k1.x-k2.x)*(k1.x-k2.x) + (k1.y-k2.y)*(k1.y-k2.y); }
//@两点距离@
db dis(point k1, point k2){ return sqrt(dis2(k1, k2)); }
//@叉乘:@
db cross(point k1, point k2){ return k1.x*k2.y - k1.y*k2.x; }
//@点乘:@
db dot(point k1, point k2){ return k1.x*k2.x + k1.y*k2.y; }
//@向量夹角:@
db rad(point k1, point k2){
return acos(dot(k1,k2)/k1.len()/k2.len());
}
//@极角排序,[-π, π]:@
bool compareangle (point k1,point k2){
return k1.getp()<k2.getp() || (k1.getp()==k2.getp() && sign(cross(k1,k2))>0);
}
bool cmp2(point a,point b){
if(sign(a.y*b.y)==0) return a.y<b.y;
if(sign(a.x*b.x)==0) return (a.x<b.x)^(a.y>-eps);
return a.y/a.x<b.y/b.x;
}
//@k1 k2 k3 逆时针1 顺时针-1 否则0:@
int clockwise(point k1,point k2,point k3){return sign(cross(k2-k1,k3-k1));}
//@直线与线段@
//@直线类@
struct line{
//@方向为p[0]->p[1]@
point p[2];
line(){}
line(db x1,db y1,db x2,db y2){p[0]=point(x1,y1),p[1]=point(x2,y2);}
line(point k1,point k2){p[0]=k1; p[1]=k2;}
point& operator [] (int k){return p[k];}
//@点在直线左侧的判定:@
//@沿着p0->p1的左侧为1,右侧为0@
bool include(point k){
return sign(cross(p[0]-k,p[1]-k))>0;
}
//@方向向量:@
point dir(){return p[1]-p[0];}
//@向外(左)平移eps@
line push(){
point delta=(p[1]-p[0]).turn90().unit()*eps;
return {p[0]-delta, p[1]-delta};
}
};
//@线段类:@
struct segment{
point p[2];
segment(){}
segment(db x1,db y1,db x2,db y2){p[0]=point(x1,y1),p[1]=point(x2,y2);}
segment(point a, point b){ p[0]= a, p[1] = b; }
point dir(){return p[1]-p[0];}
point& operator [] (int k){ return p[k]; }
};
//@q 到直线 k1,k2 的投影:@
point proj(point q, point k1, point k2){
point k=k2-k1;
return k1+k*(dot(q-k1,k)/k.len2());
}
//@q 关于直线 k1, k2 的对称点:@
point reflect(point q, point k1, point k2){
return proj(q,k1,k2)*2-q;
}
//@点在线段上的判定:@
bool checkons(point q,point k1,point k2){
return inmid(q,k1,k2) && sign(cross(k1-q, k2-k1))==0;
}
//@点在直线上的判定:@
bool checkonl(point q,point k1,point k2){
return sign(cross(k1-q, k2-k1))==0;
}
//@点在射线k1->k2上的判定:@
bool checkonr(point q, point k1, point k2){
return sign(cross(q-k1, k2-k1)) == 0 && sign(dot(q-k1, k2-k1)) >= 0;
}
//@直线平行判定,可以重合:@
bool parallel(line k1,line k2){ return sign(cross(k1.dir(),k2.dir()))==0; }
//@直线同向判定:@
bool samedir(line k1,line k2){
return parallel(k1,k2)&&sign(dot(k1.dir(),k2.dir()))==1;
}
//@直线的比较,极角排序,范围是[-π, π]:@
bool operator < (line k1,line k2){
if (samedir(k1,k2)) return k2.include(k1[0]);
return compareangle(k1.dir(),k2.dir());
}
//@直线相交判定:@
//@叉积计算面积,两直线不平行必相交(除去重合的情况),平行时,三角形面积相等:@
bool checkll(point k1,point k2,point k3,point k4){
return cmp(cross(k3-k1,k4-k1),cross(k3-k2,k4-k2))!=0;
}
//@直线相交判定:@
bool checkll(line k1,line k2){
return checkll(k1[0],k1[1],k2[0],k2[1]);
}
//@直线交点:@
point getll(point k1,point k2,point k3,point k4){
db w1=cross(k1-k3,k4-k3),w2=cross(k4-k3,k2-k3);
return (k1*w2+k2*w1)/(w1+w2);
}
//@直线交点:@
point getll(line k1,line k2){
return getll(k1[0],k1[1],k2[0],k2[1]);
}
//@直线与线段相交判定:@
//@线段的两端点在直线的两侧@
bool checkls(point k1, point k2, point k3, point k4){
return sign(cross(k1-k3, k2-k3)) * sign(cross(k1-k4, k2-k4)) <= 0;
}
//@ 线段相交判定:@
bool checkss(point k1,point k2,point k3,point k4){
return intersect(k1.x,k2.x,k3.x,k4.x)&&intersect(k1.y,k2.y,k3.y,k4.y) &&
sign(cross(k3-k1,k4-k1))*sign(cross(k3-k2,k4-k2))<=0 &&
sign(cross(k1-k3,k2-k3))*sign(cross(k1-k4,k2-k4))<=0;
}
//@ 线段相交判定:@
bool checkss(segment k1, segment k2){
return checkss(k1[0], k1[1], k2[0], k2[1]);
}
//@线段规范相交判定:@
//@端点相交不算@
bool strictcheckss(point k1, point k2, point k3, point k4){
return sign(cross(k3-k1,k4-k1))*sign(cross(k3-k2,k4-k2))<0 &&
sign(cross(k1-k3,k2-k3))*sign(cross(k1-k4,k2-k4))<0;
}
//@ 线段规范相交判定:@
bool strictcheckss(segment k1, segment k2){
return strictcheckss(k1[0], k1[1], k2[0], k2[1]);
}
//@点到直线的距离:@
db displ(point q, point k1, point k2){
if(k1 == k2) return dis(q, k1);
return fabs(cross(k2-k1, q-k1)) / (k2-k1).len();
}
//@点到直线的距离:@
db displ(point q, line l){
return displ(q, l[0], l[1]);
}
//@点到线段的距离:@
db disps(point q,point k1,point k2){
point k3 = proj(q,k1,k2);
if (inmid(k3,k1,k2)) return dis(q, k3);
else return min(dis(q, k1),dis(q, k2));
}
//@点到线段的距离:@
db disps(point q, segment k1){
return disps(q, k1[0], k1[0]);
}
//@线段到线段间的距离:@
db disss(point k1,point k2,point k3,point k4){
if (checkss(k1,k2,k3,k4)) return 0;
else return min(min(disps(k3,k1,k2),disps(k4,k1,k2)),
min(disps(k1,k3,k4),disps(k2,k3,k4)));
}
//@线段到线段间的距离:@
db disss(segment k1, segment k2){
return disss(k1[0], k1[1], k2[0], k2[1]);
}
//@多边形函数@
//@三角形面积:@
db tarea(point a,point b,point c){
return fabs((b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x))/2;
}
//@多边形面积:@
//@多边形用 vector<point> 表示 , 逆时针@
db polyarea(vector<point>A){
db ans = 0;
sort(all(A),compareangle);
for(int i=0;i<A.size();i++) ans += cross(A[i],A[(i+1)%A.size()]);
return fabs(ans/2);
}
//@多边形周长:@
db polyperimeter(vector<point>&A){
db ans = 0;
for(int i = 0; i < A.size(); i++) ans += dis(A[i], A[(i+1)%A.size()]);
return ans;
}
//@多边形重心:@
point polyfocus(vector<point>&A){
int n = A.size();
db sumx= 0, sumy = 0, sumarea = 0, area;
for(int i = 1; i+1 < n; i++){
area = cross(A[i]-A[0], A[i+1]-A[0])/2.0;
sumarea += area;
sumx += (A[0].x+A[i].x+A[i+1].x)*area;
sumy += (A[0].y+A[i].y+A[i+1].y)*area;
}
return point(sumx/sumarea/3.0, sumy/sumarea/3.0);
}
//@点与多边形的位置关系:@
//@ 2 内部 1 边界 0 外部@
int contain(vector<point>&A, point q){
int pd=0; A.push_back(A[0]);
for (int i=1;i<A.size();i++){
point u=A[i-1], v=A[i];
if (checkons(q,u,v)) return 1;
if (cmp(u.y,v.y)>0) swap(u,v);
if (cmp(u.y,q.y)>=0||cmp(v.y,q.y)<0) continue;
if (sign(cross(u-v,q-v))<0) pd^=1;
}
return pd<<1;
int wn = 0;
int n = A.size();
for(int i = 0; i < n; i++){
if(checkons(q, A[i], A[(i+1)%n])); return -1;//onside
int k = sign(cross(A[(i+1)%n]-A[i], q-A[i]));
int d1 = sign(A[i].y-q.y);
int d2 = sign(A[(i+1)%n].y-q.y);
if(k > 0 && d1 <= 0 && d2 > 0) wn++;
if(k < 0 && d2 <= 0 && d1 > 0) wn--;
}
if(wn != 0) return 1;//inside
return 0;//outside
}
//@逆时针凸包判定:@
int checkconvex(vector<point>&A){
int n=A.size(); A.pb(A[0]); A.pb(A[1]);
for (int i=0;i<n;i++) if(sign(cross(A[i+1]-A[i],A[i+2]-A[i]))==-1) return 0;
return 1;
}
//@求凸包:@
//@flag=0 不严格 flag=1 严格@
vector<point> convexhull(vector<point>A, int flag=1){
int n=A.size(); vector<point>ans(n*2);
sort(A.begin(), A.end());
int now=-1;
//@下凸壳@
for(int i=0;i<n;i++){
while(now>0 && sign(cross(ans[now]-ans[now-1], A[i]-ans[now-1])) < flag) now--;
ans[++now]=A[i];
}
int pre=now;
//@上凸壳@
for(int i=n-2;i>=0;i--){
while(now>pre && sign(cross(ans[now]-ans[now-1], A[i]-ans[now-1])) < flag) now--;
ans[++now]=A[i];
}
//@因为A[0]会被算两次,所以舍弃最后一次的A[0]@
ans.resize(now);
return ans;
}
//@切割凸包:@
//@保留直线左边的所有点@
vector<point> convexcut(vector<point>A,point k1,point k2){
int n=A.size(); A.push_back(A[0]); vector<point>ans;
for(int i=0;i<n;i++){
int w1=clockwise(k1,k2,A[i]), w2=clockwise(k1,k2,A[i+1]);
if (w1>=0) ans.push_back(A[i]);
if (w1*w2<0) ans.push_back(getll(k1,k2,A[i],A[i+1]));
}
return ans;
}
//@求半平面交:@
//@半平面是逆时针方向 , 输出按照逆时针@
vector<point> gethalf(vector<line> L){
int n = L.size();
sort(L.begin(), L.end());
int first = 0, last = 0;
//@双端队列指针@
line *q = new line[n];
//@双端队列@
point *p = new point[n];
//@p[i]为l[i]和l[i+1]的交点@
q[last] = L[0];
//@初始化为一个半平面@
for(int i = 0; i < n; i++){
while(first < last && !L[i].include(p[last-1])) last--;
while(first < last && !L[i].include(p[first])) first++;
q[++last] = L[i];
if(samedir(q[last], q[last-1])) last--;
if(first < last) p[last-1] = getll(q[last], q[last-1]);
}
while(first < last && !q[first].include(p[last-1])) last--;
vector<point>ans;
if(last - first <= 1) return ans;
p[last] = getll(q[last], q[first]);
for(int i = first; i <= last; i++) ans.pb(p[i]);
return ans;
}
int checkpos(line k1,line k2,line k3){return k3.include(getll(k1,k2));}
//@求半平面交:@
//@半平面是逆时针方向 , 输出按照逆时针@
vector<line> gethl(vector<line> L){
sort(L.begin(),L.end()); deque<line> q;
for (int i=0;i<(int)L.size();i++){
if (i&&samedir(L[i],L[i-1])) continue;
while (q.size()>1&&!checkpos(q[q.size()-2],q[q.size()-1],L[i])) q.pop_back();
while (q.size()>1&&!checkpos(q[1],q[0],L[i])) q.pop_front();
q.push_back(L[i]);
}
while (q.size()>2&&!checkpos(q[q.size()-2],q[q.size()-1],q[0])) q.pop_back();
while (q.size()>2&&!checkpos(q[1],q[0],q[q.size()-1])) q.pop_front();
vector<line>ans; for (int i=0;i<q.size();i++) ans.push_back(q[i]);
return ans;
}
//@凸包最近点对:@
//@先要按照 x 坐标排序@
bool _cmp(point k1,point k2){return k1.y<k2.y;}
db closestpoint(vector<point>&A,int l,int r){
if (r-l<=5){
//@当点数小于等于5时,暴力计算:@
db ans=1e20;
for (int i=l;i<=r;i++) for (int j=i+1;j<=r;j++) ans=min(ans,dis(A[i],A[j]));
return ans;
}
int mid=l+r>>1; db ans=min(closestpoint(A,l,mid),closestpoint(A,mid+1,r));
vector<point>B; for (int i=l;i<=r;i++) if (abs(A[i].x-A[mid].x)<=ans) B.push_back(A[i]);
sort(B.begin(),B.end(),_cmp);
for (int i=0;i<B.size();i++) for (int j=i+1;j<B.size()&&B[j].y-B[i].y<ans;j++) ans=min(ans,dis(B[i],B[j]));
return ans;
}
//@凸包的直径(最远点对):@
db convexdiameter(vector<point>&A){
int n = A.size();
int now = 1;
db res = 0;
for(int i = 0; i < n; i++){
while(cross(A[i]-A[(i+1)%n], A[i]-A[(now+1)%n]) > cross(A[i]-A[(i+1)%n], A[i]-A[now])){
now = (now+1)%n;
}
res = max(res, dis2(A[now], A[i]));
}
return res;
}
//@点集中的最大三角形:@
db maxtriangle(vector<point>&A){
int m = A.size();
int a = 1, b = 2;
db res = 0;
for(int i = 0; i < m; i++){
while(cross(A[a]-A[i], A[(b+1)%m]-A[i]) > cross(A[a]-A[i], A[b]-A[i]))
b = (b + 1) % m;
res = max(res, cross(A[a]-A[i], A[b]-A[i]) / 2.0);
while(cross(A[(a+1)%m]-A[i], A[b]-A[i]) > cross(A[a]-A[i], A[b]-A[i]))
a = (a + 1) % m;
res = max(res, cross(A[a]-A[i], A[b]-A[i]) / 2.0);
}
return res;
}
//@凸包间的最小距离:@
db mindisbetconvex(vector<point>&A, vector<point>&B){
int n = A.size(), m = B.size();
if(n < 3 && m < 3){
if(n == 1){
if(m == 1) return dis(A[0], B[0]);
else return disps(A[0], B[0], B[1]);
}
else{
if(m == 1) return disps(B[0], A[0], A[1]);
else return disss(A[0], A[1], B[0], B[1]);
}
}
int ai = 0, bi = 0;
for(int i = 0; i < n; i++) if(A[i].y < A[ai].y){ ai = i; }
for(int i = 0; i < m; i++) if(B[i].y > A[bi].y){ bi = i; }
db ans = 1e18;
for(int i = 0; i < n; i++){
db ck;
while(ck = sign(cross(B[(bi+1)%m]-B[bi], A[(ai+1)%n]-A[ai])) < 0) bi = (bi+1)%m;
if(ck == 0) ans = min(ans, disss(A[(ai+1)%n], A[ai], B[(bi+1)%m], B[bi]));
else ans = min(ans, disps(B[bi], A[(ai+1)%n], A[ai]));
ai = (ai+1)%n;
}
return ans;
}
//@最小正方形覆盖:@
db minsquarecover(vector<point>&A, db rad){
db minx = inf, maxx = -inf, miny = inf, maxy = -inf;
for(int i = 0; i < A.size(); i++){
point p = A[i].turn(rad);
minx = min(minx, p.x);
miny = min(miny, p.y);
maxx = max(maxx, p.x);
maxy = max(maxy, p.y);
}
return max(maxx-minx, maxy-miny);
}
//@三分--最小正方形覆盖:@
db t_divide(vector<point>&A, db l, db r){
db m, rm, eps = 1e-8;
while(r-l >= eps){
m = l+(r-l)/3;
rm = r-(r-l)/3;
if(minsquarecover(A, m) > minsquarecover(A, rm)) l = m;
else r = rm;
}
return minsquarecover(A, (m+rm)/2);
}
vector<line> seg;
vector<int> ans;
db c[maxn];
int main() {IO;
cout<<fixed<<setprecision(4);
db x,y,dx,dy;
cin>>x>>y>>dx>>dy>>n;
point now(x,y),v0(dx,dy);
line s(now,now+v0);
rep(i,1,n){
db x1,x2,y1,y2,a,b;
cin>>x1>>y1>>x2>>y2>>a>>b;
c[i-1]=a/b;
seg.push_back(line(x1,y1,x2,y2));
}
rep(_,1,10){
int flag=0,id;
db mn=1e100;
rep(j,0,n-1){
if(parallel(s,seg[j])) continue;
point x=getll(s,seg[j]);
if(checkons(x,seg[j][0],seg[j][1])&&sign(dot(s.dir(),x-now))>0){
db d=(x-now).len();
if(d<mn)mn=d,id=j;
flag=1;
}
}
if(!flag) break;
else {
cout<<id+1<<' ';
ans.push_back(id+1);
now=getll(s,seg[id]);
if(sign(dot(seg[id].dir(),s.dir()))==0){
s=line(now,now-s.dir());
continue;
}
point v1=seg[id].dir();
if(sign(dot(v1,s.dir()))<0) v1=v1*-1;
db al=pi/2-rad(v1,s.dir());
int bt=sign(cross(v1,s.dir()));
s=line(now,now+v1.turn(bt*(al*c[id]-pi/2)));
}
}
if(ans.size()==0)cout<<"NONE";
return 0;
}

浙公网安备 33010602011771号