1 条题解

  • 0
    @ 2026-4-27 22:35:45

    思路

    先干一件很重要但不难理解的事:题目里的平面是无限的,这太烦了,那我们直接在外面套一个特别大的正方形,把所有东西都关进去。反正 LL 给得够大,答案不会受影响,这一步就是为了让后面所有区域都有“边界”。

    接下来就是这题真正的灵魂:扫线。

    你可以把所有直线按斜率排个序,想象从左往右慢慢扫过去。正常情况下,直线的上下顺序是不会乱的,只有在两条线“相交”的那一瞬间,它们才会换个位置。

    所以事情就简单了:我们提前把所有直线两两的交点全算出来,然后按顺序一个一个处理。每次扫到一个交点,只会影响那两条线,中间夹着的那一小块区域发生变化,别的地方一概不管。

    查询点怎么处理?也很舒服。扫到某个位置的时候,当前直线顺序已经是确定的了,那我只要二分一下,看这个点在第几条线之间,就知道它属于哪个区域了。先记下来,等这块区域的面积算完再统一结算。

    面积怎么算?这里用的是叉积。你可以把它理解成:扫线的时候,区域一点一点“长出来”,每次只加一个小三角形或者小多边形,叉积正好能又快又准地算这个面积。

    整个过程就像洗牌:遇到交点,算那一小块,交换两条线,继续扫。

    最后所有查询点都已经挂在对应的区域上了,面积也都算完了,直接输出就行。

    代码

    #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

    「CodePlus 2017 12 月赛」寄蒜几盒

    信息

    ID
    11301
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者