题解扫线
2026-02-06 19:39:10
发布于:湖南
1阅读
0回复
0点赞
思路
先干一件很重要但不难理解的事:题目里的平面是无限的,这太烦了,那我们直接在外面套一个特别大的正方形,把所有东西都关进去。反正 L 给得够大,答案不会受影响,这一步就是为了让后面所有区域都有“边界”。
接下来就是这题真正的灵魂:扫线。
你可以把所有直线按斜率排个序,想象从左往右慢慢扫过去。正常情况下,直线的上下顺序是不会乱的,只有在两条线“相交”的那一瞬间,它们才会换个位置。
所以事情就简单了:我们提前把所有直线两两的交点全算出来,然后按顺序一个一个处理。每次扫到一个交点,只会影响那两条线,中间夹着的那一小块区域发生变化,别的地方一概不管。
查询点怎么处理?也很舒服。扫到某个位置的时候,当前直线顺序已经是确定的了,那我只要二分一下,看这个点在第几条线之间,就知道它属于哪个区域了。先记下来,等这块区域的面积算完再统一结算。
面积怎么算?这里用的是叉积。你可以把它理解成:扫线的时候,区域一点一点“长出来”,每次只加一个小三角形或者小多边形,叉积正好能又快又准地算这个面积。
整个过程就像洗牌:遇到交点,算那一小块,交换两条线,继续扫。
最后所有查询点都已经挂在对应的区域上了,面积也都算完了,直接输出就行。
代码
#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;
}
这里空空如也






有帮助,赞一个