题面
题解
先对于每个 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\n",ans);
}
return 0;
}
/*
2 1 1 0
0 8
1 7
2 114514 6
8 -1
*/
我傻了,不应该用 double
直接算极角的,因为这样精度要开到很小才能过,其实直接用叉乘来判断两条线的极角顺序就好了。