题目链接:https://ac.nowcoder.com/acm/contest/882/G
题目大意:有(n)条直线将平面分成若干个区域,要求处理(m)次询问:求第(q)大的区域面积。保证没有三线共点或者两直线重合,(nleq 1000, mleq 10000)
题解:先考虑最多会有多少个区域,由于第(i)条直线最多与前面的(i-1)条直线同时相交,所以交点个数和区域个数都是(n^2)级别的,考虑求出所有区域的面积并排序
首先预处理所有的交点,并记录与该交点相邻的交点,在他们之间建两条有向边(即记录两个向量)。由于没有三线共点的情况,所以每个点的度数均不超过(4)。每次选取一个点作为起点,并选取另一个与之相邻的点作为当前点,从当前点出发寻找一个没被使用过且与当前边的向量夹角最大(或最小)的边,并进入下一个点,直至回到原点。这样就能找出其中的一个区域。执行这样的算法若干次直至所有边均被使用过就能求出所有的区域面积,注意判断当前区域不合法的情况即可。
关于为什么每次找夹角最大(或最小)的边能找到所有区域,是因为这个过程实际上是平面图转对偶图的算法实现(具体见http://blog.miskcoo.com/2015/05/planar-graph-dual-and-point-locate 这篇博客的讲解,感谢qls深夜给我普及这一知识点),因此如果每次找夹角最小的边是显然正确的。关于找夹角最大的正确性我想是因为,由于每个交点最多是两条直线的交点,所以最小角和最大角的区别就是左转和右转, 也是满足这一算法的要求的。
对于这题的代码实现,我的做法是对于每条直线先预处理所有交点坐标并排好序(可以用点乘实现),然后就可以处理出和每个交点相邻的点是哪几个。由于每次只可能有两个合法的相邻点,所以可以直接用叉积的正负来判断下一个要走哪个点,剩下的就是判断合法性的问题了
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 1001 4 #define mp make_pair 5 const double eps=1e-12; 6 int n,m,q,cnt,f[N*N],poly[N]; 7 vector<pair<int,bool>>d[N*N]; 8 vector<pair<double,int>>l[N]; 9 vector<double>ans; 10 struct Point 11 { 12 double x,y; 13 void read(){scanf("%lf%lf",&x,&y);} 14 Point operator +(const Point &t)const{return {x+t.x,y+t.y};} 15 Point operator -(const Point &t)const{return {x-t.x,y-t.y};} 16 double operator *(const Point &t)const{return x*t.y-y*t.x;} 17 double operator %(const Point &t)const{return x*t.x+y*t.y;} 18 Point operator *(double t)const{return {x*t,y*t};} 19 Point operator /(double t)const{return {x/t,y/t};} 20 }p[N*N]; 21 struct Line 22 { 23 Point p1,p2; 24 void read(){p1.read(),p2.read();} 25 bool intersect(const Line &t)const 26 {return fabs((p2-p1)*(t.p2-t.p1))>eps;} 27 Point isct(const Line &t)const 28 { 29 double a=(p2-p1)*(t.p1-p1); 30 double b=(p2-p1)*(p1-t.p2); 31 return (t.p1*b+t.p2*a)/(a+b); 32 } 33 }a[N]; 34 void del(int st,int ed) 35 { 36 for(int i=0;i<d[st].size();i++) 37 if(d[st][i].first==ed) 38 { 39 d[st][i].second=false; 40 f[st]--; 41 return; 42 } 43 } 44 int main() 45 { 46 scanf("%d",&n); 47 for(int i=1;i<=n;i++) 48 a[i].read(); 49 for(int i=1;i<=n;i++) 50 for(int j=i+1;j<=n;j++) 51 if(a[i].intersect(a[j])) 52 { 53 p[++cnt]=a[i].isct(a[j]); 54 l[i].push_back(mp((p[cnt]-a[i].p1)%(a[i].p2-a[i].p1),cnt)); 55 l[j].push_back(mp((p[cnt]-a[j].p1)%(a[j].p2-a[j].p1),cnt)); 56 } 57 for(int i=1;i<=n;i++) 58 { 59 sort(l[i].begin(),l[i].end()); 60 for(int j=1;j<l[i].size();j++) 61 { 62 d[l[i][j].second].push_back(mp(l[i][j-1].second,true)); 63 d[l[i][j-1].second].push_back(mp(l[i][j].second,true)); 64 f[l[i][j].second]++; 65 f[l[i][j-1].second]++; 66 } 67 } 68 for(int i=1;i<=cnt;i++) 69 while(f[i]) 70 { 71 int pcnt=0,st=i,pre=i,cur=-1; 72 poly[++pcnt]=st; 73 for(auto pi:d[st]) 74 if(pi.second) 75 { 76 cur=poly[++pcnt]=pi.first; 77 break; 78 } 79 while(cur!=st) 80 { 81 int id=-1;double mx=-1.0; 82 for(auto pi:d[cur]) 83 if(pi.second) 84 { 85 int nxt=pi.first; 86 if(nxt==pre)continue; 87 double tmp=(p[cur]-p[pre])*(p[nxt]-p[cur]); 88 if(tmp>mx)mx=tmp,id=nxt; 89 } 90 if(id<0 || mx<eps) 91 { 92 for(int i=1;i<pcnt;i++) 93 del(poly[i],poly[i+1]); 94 pcnt=0; 95 break; 96 } 97 poly[++pcnt]=id; 98 pre=cur,cur=id; 99 } 100 if(!pcnt)continue; 101 double res=0; 102 for(int i=1;i<=pcnt;i++) 103 { 104 res+=p[poly[i]]*p[poly[i%pcnt+1]]; 105 del(poly[i],poly[i%pcnt+1]); 106 } 107 res*=0.5; 108 ans.push_back(fabs(res)); 109 } 110 sort(ans.begin(),ans.end()); 111 int sz=ans.size(); 112 printf("%d %.6f %.6f ",sz,ans[sz-1],ans[0]); 113 scanf("%d",&m); 114 while(m--) 115 { 116 scanf("%d",&q); 117 if(q>sz){printf("Invalid question ");continue;} 118 printf("%.6f ",ans[sz-q]); 119 } 120 }