1 条题解
-
0
思路
先干一件很重要但不难理解的事:题目里的平面是无限的,这太烦了,那我们直接在外面套一个特别大的正方形,把所有东西都关进去。反正 给得够大,答案不会受影响,这一步就是为了让后面所有区域都有“边界”。
接下来就是这题真正的灵魂:扫线。
你可以把所有直线按斜率排个序,想象从左往右慢慢扫过去。正常情况下,直线的上下顺序是不会乱的,只有在两条线“相交”的那一瞬间,它们才会换个位置。
所以事情就简单了:我们提前把所有直线两两的交点全算出来,然后按顺序一个一个处理。每次扫到一个交点,只会影响那两条线,中间夹着的那一小块区域发生变化,别的地方一概不管。
查询点怎么处理?也很舒服。扫到某个位置的时候,当前直线顺序已经是确定的了,那我只要二分一下,看这个点在第几条线之间,就知道它属于哪个区域了。先记下来,等这块区域的面积算完再统一结算。
面积怎么算?这里用的是叉积。你可以把它理解成:扫线的时候,区域一点一点“长出来”,每次只加一个小三角形或者小多边形,叉积正好能又快又准地算这个面积。
整个过程就像洗牌:遇到交点,算那一小块,交换两条线,继续扫。
最后所有查询点都已经挂在对应的区域上了,面积也都算完了,直接输出就行。
代码
#include<cstdio> #include<cstdlib> #include<cstring> #include<vector> #include<iostream> #include<algorithm> using namespace std; #define eps 1e-7 bool same(double a,double b) { return (a-b>-eps && a-b<eps); } int n,m,t; double x,y,z; double L; double ans[100010]; struct point{double x,y;}; struct et{point s;int id;} q[100010]; struct line { double A,B,C,k; void create(double a,double b,double c) { A=a,B=b,C=c; if (same(b,0)) k=1e50;else k=-a/b; } } l[510]; struct it{int l1,l2;point c;} c[260000]; bool operator <(line a,line b) { if (same(a.k,b.k)) { if (!same(a.B,0)) return a.C/a.B<b.C/b.B; return a.C/a.A>b.C/b.A; } return a.k<b.k; } bool operator <(point a,point b) { if (same(a.x,b.x)) return a.y<b.y; return a.x<b.x; } bool operator <(et a,et b) { return a.s<b.s; } bool operator <(it a,it b) { if (same(a.c.x,b.c.x) && same(a.c.y,b.c.y)) { if (a.l1==b.l1) return a.l2<b.l2; return a.l1<b.l1; } return a.c<b.c; } point cp(line a,line b) { point ret; ret.y=(b.C*a.A-a.C*b.A)/(a.B*b.A-a.A*b.B); if (!same(a.A,0)) ret.x=-(ret.y*a.B+a.C)/a.A; else ret.x=-(ret.y*b.B+b.C)/b.A; return ret; } double cross(point a,point b) { return a.x*b.y-a.y*b.x; } double count(line a,point p) { return a.A*p.x+a.B*p.y+a.C; } void init() { l[n].create(-1,0,L); l[n+1].create(-1,0,-L); l[n+2].create(0,1,-L); l[n+3].create(0,1,L); n+=4; sort(l,l+n); t=0; for (int i=0;i<n;i++) for (int j=i+1;j<n;j++) if (!same(l[i].k,l[j].k)) { c[t].l1=i,c[t].l2=j,c[t].c=cp(l[i],l[j]); t++; } sort(q,q+m); sort(c,c+t); } int xl[510],fd[510]; double s[510]; vector<int> que[510]; point last[510]; int bs(point p) { int L=0,R=n,mid; while (L<R) { mid=(L+R)/2; if (count(l[xl[mid]],p)>0) R=mid;else L=mid+1; } return L; } void updata(int x) { for (int i=0;i<que[x].size();i++) ans[que[x][i]]=s[x]; s[x]=0; que[x].clear(); } void solve() { int j=0; for (int i=0;i<n;i++) xl[i]=i,fd[i]=i; for (int i=0;i<t;i++) { if (i==51) { j++; j--; } while (q[j].s<c[i].c && j<m) { int x=bs(q[j].s); que[x].push_back(q[j].id); j++; } int a=fd[c[i].l1],b=fd[c[i].l2]; double xa=cross(c[i].c,last[a]),xb=cross(last[b],c[i].c); if (a+1!=b) printf("wa on %d\n",i); s[b]-=xa+xb; updata(b); s[a]+=xa; s[b+1]+=xb; last[a]=last[b]=c[i].c; swap(xl[a],xl[b]); fd[xl[a]]=a; fd[xl[b]]=b; } } int main() { scanf("%d%d%lf",&n,&m,&L); for (int i=0;i<n;i++) { scanf("%lf%lf%lf",&x,&y,&z); if (same(y,0)) { if (x>0) x=-x,z=-z; } else if (y<0) x=-x,y=-y,z=-z; l[i].create(x,y,z); } for (int i=0;i<m;i++) scanf("%lf%lf",&q[i].s.x,&q[i].s.y); for (int i=0;i<m;i++) q[i].id=i; init(); solve(); for (int i=0;i<m;i++) printf("%.2lf\n",ans[i]/(-2)); return 0; }
- 1
信息
- ID
- 11301
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者