[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);
}
View Code
原文地址:https://www.cnblogs.com/hfctf0210/p/10859242.html