[GX/GZOI2019]特技飞行(扫描线+置换)
感觉是6题中最难的一题,其实这题是一个二合一:
第一问:给定平面上若干点和k个关键点,关键点覆盖一个45°倾斜的正方形范围r,求有多少点被至少一个关键点覆盖。这个可以曼哈顿转切比雪夫距离,然后再扫描线求解,复杂度O(nlogn)
第二问:求最少和最多有多少次擦肩而过。显然每个交点都可以做对向交换,这是最少擦肩而过的次数。最多的次数,假设全部擦肩而过得到排列p,对向交换实际上是交换两元素位置,用最小交换次数还原排列即可。
#include<bits/stdc++.h> using namespace std; typedef pair<int,int>pii; const int N=5e5+7; int n,m,a,b,c,x0,x1,ans1,ans2,tot,cnt,ans,C[N<<1],y[N][2],f[N],vis[N]; double d[N<<1]; set<pii>S; struct node{double x,y;int v,p;}p[N<<1]; bool operator<(node a,node b){return a.x<b.x;} node calc(int i,int j) { int y1=y[j][0]-y[i][0],y2=y[i][1]-y[j][1]; double k=1.0*y1/(y1+y2),xl=1.0*x0+k*(x1-x0),yl=1.0*y[i][0]+k*(y[i][1]-y[i][0]); d[++cnt]=xl-yl; return(node){xl+yl,xl-yl,0,0}; } bool cmp(node a,node b){return fabs(a.x-b.x)<1e-10?a.y<b.y:a.x<b.x;} void add(int x,int v){while(x<=cnt)C[x]+=v,x+=x&-x;} int query(int x){int ret=0;while(x)ret+=C[x],x-=x&-x;return ret;} bool cmp1(int a,int b){return y[a][1]<y[b][1];} int main() { scanf("%d%d%d%d%d%d",&n,&a,&b,&c,&x0,&x1); for(int i=1;i<=n;i++)scanf("%d",&y[i][0]); for(int i=1;i<=n;i++)scanf("%d",&y[i][1]); for(int i=1;i<=n;i++) { pii u=make_pair(y[i][1],i); set<pii>::iterator it=S.lower_bound(u); while(it!=S.end())p[++tot]=calc(it->second,i),it++; S.insert(u); } int sum=tot;scanf("%d",&m); for(int i=1,x,y,z;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); d[++cnt]=x-y+z,d[++cnt]=x-y-z; p[++tot]=(node){x+y+z,x-y+z,1,0}; p[++tot]=(node){x+y+z,x-y-z,-1,0}; p[++tot]=(node){x+y-z,x-y+z,-1,0}; p[++tot]=(node){x+y-z,x-y-z,1,0}; } sort(p+1,p+tot+1); sort(d+1,d+cnt+1); cnt=unique(d+1,d+cnt+1)-d-1; for(int i=1;i<=tot;i++)p[i].p=upper_bound(d+1,d+cnt+1,p[i].y-1e-10)-d; sort(p+1,p+tot+1,cmp); for(int i=1;i<=tot;i++)if(p[i].v)add(p[i].p,p[i].v);else ans+=query(p[i].p)>0; ans1=ans*c+sum*a; for(int i=1;i<=n;i++)f[i]=i; sort(f+1,f+n+1,cmp1); int num=n; for(int i=1;i<=n;i++) if(!vis[i]) { num--; for(int j=i;!vis[j];j=f[j])vis[j]=1; } ans2=ans1+(b-a)*(sum-num); if(ans1>ans2)swap(ans1,ans2); printf("%d %d",ans1,ans2); }