洛谷P4169 [Violet]天使玩偶【CDQ分治】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/niiick/article/details/82192221

时空限制 2000ms-3000ms / 128MB
题目描述
Ayu 在七年前曾经收到过一个天使玩偶,当时她把它当作时间囊埋在了地下。而七年后 的今天,Ayu 却忘了她把天使玩偶埋在了哪里,所以她决定仅凭一点模糊的记忆来寻找它。

我们把 Ayu 生活的小镇看作一个二维平面坐标系,而 Ayu 会不定时地记起可能在某个点 (xmy) 埋下了天使玩偶;或者 Ayu 会询问你,假如她在 (x,y) ,那么她离近的天使玩偶可能埋下的地方有多远。

因为 Ayu 只会沿着平行坐标轴的方向来行动,所以在这个问题里我们定义两个点之间的距离为dist(A,B)=|Ax-Bx|+|Ay-By|。其中 Ax 表示点 A的横坐标,其余类似。

输入格式:

第一行包含两个整数n和m ,在刚开始时,Ayu 已经知道有n个点可能埋着天使玩偶, 接下来 Ayu 要进行m 次操作

接下来n行,每行两个非负整数 (xi,yi),表示初始n个点的坐标。

再接下来m 行,每行三个非负整数 t,xi,yi。
如果t=1 ,则表示 Ayu 又回忆起了一个可能埋着玩偶的点 (xi,yi) 。
如果t=2 ,则表示 Ayu 询问如果她在点 (xi,yi) ,那么在已经回忆出来的点里,离她近的那个点有多远

输出格式:

对于每个t=2 的询问,在单独的一行内输出该询问的结果。

说明

n,m<=300 000
xi,yi<=1 000 000


题目分析

对于一个询问点 ( x i , y i )
假如我们只考虑其左下角与其最近的点
那么有 | x i x j | + | y i y j | = ( x i + y i ) ( x j + y j )
也就是说我们只需要找到一个 j
使得 x j <= x i , y j <= y i x j + y j 最大
就找到了 ( x i , y i ) 左下角离他最近的点

当然查找到的点必须在该询问点之前已被给出
所以我们给每个点加上时间戳,对于上述还要满足 t j < t i

到这里三维偏序已经很明显了,可以用CDQ分治
注意只查询一次左下角肯定不对
但我们可以通过旋转坐标四个方位都转化为左下角查询
四次 CDQ分治

这题在luogu时间卡的hin紧
尽管各种乱搞优化,最后还是吸了氧才过

至于有什么乱搞优化
1.每次CDQ前按时间戳排序很浪费时间,读入用临时数组,CDQ前再复制一遍
2.每次CDQ前把肯定不会在某个询问左下角的点删掉


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
#define lowbit(x) ((x)&(-x)) 

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return x*f;
}

const int maxn=600010;
int n,m,cnt;
struct node{int t,x,y,f;}a[maxn],b[maxn],c[maxn];
int mx[maxn*5],mxy=-1e9;
int ans[maxn],rem[maxn];

int cmin(int x,int y){return x<y?x:y;}
int cmax(int x,int y){return x>y?x:y;}

void add(int x,int v){ for(int i=x;i<=mxy;i+=lowbit(i))mx[i]=cmax(mx[i],v);}
int query(int x){ int res=0; for(int i=x;i>0;i-=lowbit(i))res=cmax(res,mx[i]); return res;}
void cls(int x){ for(int i=x;i<=mxy;i+=lowbit(i))mx[i]=0;}

void CDQ(int ll,int rr)
{
    if(ll==rr) return;
    int mid=ll+rr>>1;
    CDQ(ll,mid); CDQ(mid+1,rr);

    int t1=ll,t2=mid+1,p=ll;
    while(t2<=rr)
    {
        while(a[t1].x<=a[t2].x&&t1<=mid) 
        {
            if(a[t1].f==1)add(a[t1].y,a[t1].x + a[t1].y);
            b[p++]=a[t1++];
        }
        if(a[t2].f==2) 
        {
            int tt=query(a[t2].y);
            if(tt)ans[a[t2].t]=cmin( ans[a[t2].t], a[t2].x+a[t2].y-tt); 
        }
        b[p++]=a[t2++];
    }

    for(int i=ll;i<t1;++i)
    if(a[i].f==1)cls(a[i].y);

    while(t1<=mid) b[p++]=a[t1++];
    while(t2<=rr) b[p++]=a[t2++];
    for(int i=ll;i<=rr;++i) a[i]=b[i];
}

void init(int d)
{
    int mxx=0,myy=0; cnt=0;
    for(int i=1;i<=n+m;++i)
    {
        if(d==1) c[i].x=mxy-c[i].x;
        else if(d==2) c[i].y=mxy-c[i].y;
        if(c[i].f==2)mxx=cmax(mxx,c[i].x),myy=cmax(myy,c[i].y);
    }

    for(int i=1;i<=n+m;++i)
    if(c[i].x<=mxx&&c[i].y<=myy)
    a[++cnt]=c[i];
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)
    {
        c[i].x=read()+1; c[i].y=read()+1,
        c[i].t=i; c[i].f=1; 
        mxy=cmax(mxy,cmax(c[i].x,c[i].y));
    }

    for(int i=n+1;i<=m+n;++i)
    {
        c[i].f=read(); c[i].x=read()+1; c[i].y=read()+1; c[i].t=i;
        if(c[i].f==2) rem[++rem[0]]=c[i].t,ans[c[i].t]=1e9;
        mxy=cmax(mxy,cmax(c[i].x,c[i].y)); 
    }

    mxy++; 
    init(0); CDQ(1,cnt);
    init(1); CDQ(1,cnt);
    init(2); CDQ(1,cnt);
    init(1); CDQ(1,cnt);

    for(int i=1;i<=rem[0];++i)
    printf("%d\n",ans[rem[i]]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/niiick/article/details/82192221