【XSY3812】鱼死网破(计算几何?)

题面

在这里插入图片描述
在这里插入图片描述

题解

先对于每个 x x x 轴上方的点,找到所有墙,按照极角排序,合并重合的墙。

对于每一个端点引一条射线,左端点的射线权值是 + 1 +1 +1,右端点是 − 1 -1 1,可以发现一个点不能看到的点数等于这个点左边的射线的权值和。

观察到这些直线都经过至少一个墙的端点,可以将这些直线按照经过的端点分类,每一类内极角排序。然后查询的时候对于每一堵墙二分查询就好了。

时间复杂度 O ( k ( n + m ) log ⁡ n ) O(k(n+m)log n) O(k(n+m)logn)

代码如下:

#include<bits/stdc++.h>

#define K 55
#define N 100010
#define eps 1e-11

using namespace std;

int compare(double a,double b)
{
	if(fabs(a-b)<eps) return 0;
	return a<b?(-1):1;
}

struct Point
{
	int x,y;
	Point(){};
	Point(int a,int b){x=a,y=b;}
	double get(){return atan2(y,x);}
}a[N],wall[K][2];

Point operator - (Point a,Point b)
{
	return Point(a.x-b.x,a.y-b.y);
}

struct Line
{
	double deg;
	int val,id;
	Line(){};
	Line(double a,int b,int c){deg=a,val=b,id=c;}
}l[N][K<<1];

bool operator < (Line a,Line b)
{
	if(!compare(a.deg,b.deg)) return a.val>b.val;
	return compare(a.deg,b.deg)==-1;
}

int n,m,k,opt;

vector<double>deg[K][2];

bool cmp(double a,double b)
{
	return compare(a,b)==-1;
}

int upper(int i,double x)
{
	int l=0,r=deg[i][0].size()-1,ans=-1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(compare(deg[i][0][mid],x)!=1) ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans+1;
}

int lower(int i,double x)
{
	int l=0,r=deg[i][1].size()-1,ans=-1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(compare(deg[i][1][mid],x)==-1) ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans+1;
}

int main()
{
	scanf("%d%d%d%d",&n,&k,&m,&opt);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&a[i].x,&a[i].y);
	for(int i=1;i<=k;i++)
	{
		scanf("%d%d%d",&wall[i][0].x,&wall[i][1].x,&wall[i][0].y);
		wall[i][1].y=wall[i][0].y;
		if(wall[i][0].x>wall[i][1].x) swap(wall[i][0].x,wall[i][1].x);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=k;j++)
		{
			l[i][(j<<1)-1]=Line((wall[j][0]-a[i]).get(),1,j);
			l[i][j<<1]=Line((wall[j][1]-a[i]).get(),-1,j);
		}
		sort(l[i]+1,l[i]+(k<<1)+1);
		int tmp=0;
		for(int j=1;j<=(k<<1);j++)
		{
			if(l[i][j].val==1)
			{
				if(!tmp) deg[l[i][j].id][0].push_back(l[i][j].deg);
				tmp++;
			}
			else
			{
				tmp--;
				if(!tmp) deg[l[i][j].id][1].push_back(l[i][j].deg);
			}
		}
	}
	for(int i=1;i<=k;i++)
		for(int j=0;j<2;j++)
			sort(deg[i][j].begin(),deg[i][j].end(),cmp);
	int ans=0;
	for(int i=1;i<=m;i++)
	{
		Point b;
		scanf("%d%d",&b.x,&b.y);
		if(opt) b.x^=ans,b.y^=ans;
		ans=n;
		for(int j=1;j<=k;j++)
		{
			ans-=upper(j,(b-wall[j][0]).get());
			ans+=lower(j,(b-wall[j][1]).get());
		}
		printf("%d
",ans);
	}
	return 0;
}
/*
2 1 1 0
0 8
1 7
2 114514 6
8 -1
*/

我傻了,不应该用 double直接算极角的,因为这样精度要开到很小才能过,其实直接用叉乘来判断两条线的极角顺序就好了。

原文地址:https://www.cnblogs.com/ez-lcw/p/14448632.html