题目描述
由于领土纠纷,胖头鱼联邦打算偷袭鱼头胖共和国。由于两国关系本身十分紧张,所以双方都在边界上部署了很多士兵。
两个国家非常大,可以抽象成二维平面上的两个无穷大区域,鱼头胖共和国在 $x$ 轴上方,胖头鱼联邦在 $x$ 轴下方, $x$ 轴不属于任何一个国家。胖头鱼有 $n$ 个士兵,鱼头胖有 $m$ 个士兵,士兵都在自己领土内,任意两个士兵的坐标不会重合。
为了成功偷袭,胖头鱼必须先击倒鱼头胖的士兵。胖头鱼士兵可以使用高压水枪射击鱼头胖士兵,但是鱼头胖在自己领土上设置了 $k$ 道防水墙,一个胖头鱼士兵能攻击鱼头胖士兵当且仅当两个士兵的连线上没有防水墙(包括顶点)。防水墙可以抽象成一条平行于 $x$ 轴的线段,可以用一个三元组 $(a,b,c)$ 表示,即这个防水墙的两个端点的坐标是 $(a,c),(b,c)$ ,保证这个没有士兵在这条线段上。
每个胖头鱼士兵想知道自己能攻击多少个鱼头胖士兵。你必须逐个回答胖头鱼士兵的问题。强制在线!
数据范围
$n,m \le 10^5$ ; $k \le 50$ ; 所有点坐标绝对值 $\le 10^6$
题解
考虑每个 $x$ 轴上方的点,对于它下方的墙的两个端点构成两条射线,把左端点的射线权值设为 $1$ ,右端点的射线权值设为 $-1$ ,对于每个 $x$ 轴下方的点,找到每个墙的每个端点,每个端点上会有一些射线,对这个点有贡献的是在其左方的射线,故极角排序后二分即可。效率: $O(k(n+m)logn)$
代码
#include <bits/stdc++.h> #define LL long long using namespace std; const int N=1e5+5; int n,k,m,op,t,w[105],sz[105]; struct O{ int x,y; friend O operator - (O A,O B){ return (O){A.x-B.x,A.y-B.y}; } friend LL operator * (O A,O B){ return 1ll*A.x*B.y-1ll*A.y*B.x; } }a[N],b[105],nw; struct P{O u;int i;}g[105]; vector<O>p[N]; bool cmp(P A,P B){ LL v=(A.u-nw)*(B.u-nw); return v>0 || (v==0 && w[A.i]>w[B.i]); } bool Cmp(O A,O B){return A*B>0;} int main(){ cin>>n>>k>>m>>op; for (int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y); for (int i=1,x,y,z;i<=k;i++){ scanf("%d%d%d",&x,&y,&z); w[++t]=1;b[t]=(O){x,z}; w[++t]=-1;b[t]=(O){y,z}; } for (int c,x,i=1;i<=n;i++){ c=0;nw=a[i];x=0; for (int j=1;j<=t;j++) if (a[i].y>b[j].y) g[++c]=(P){b[j],j}; sort(g+1,g+c+1,cmp); for (int j=1;j<=c;j++){ if (x==0 || (x==1 && w[g[j].i]==-1)) p[g[j].i].push_back(g[j].u-nw),sz[g[j].i]++; x+=w[g[j].i]; } } for (int i=1;i<=t;i++) sort(p[i].begin(),p[i].end(),Cmp); for (int x,y,lst=0;m--;){ scanf("%d%d",&x,&y); if (op) x^=lst,y^=lst;lst=0; for (int l,r,i=1;i<=t;i++){ if (!sz[i]) continue; nw=(O){x,y}-b[i]; l=-1;r=sz[i]-1; while(l<r){ int mid=(l+r+1)>>1; if (((~w[i])?nw*p[i][mid]>0:nw*p[i][mid]>=0)) r=mid-1; else l=mid; } lst+=w[i]*(l+1); } lst=n-lst;printf("%d\n",lst); } return 0; }