BZOJ2716天使玩偶

不会KD-tree怎么办?CQD硬搞。

建立正常的平面直角坐标系,首先我们只考虑在目标点左下角的点对目标点的贡献,由于左下点的横纵坐标都小于目标点,那么曼哈顿距离就可以化简了,绝对值去掉后,得到$x2+y2-(x1+y1)$,那么我们的目标就转化为了求横纵坐标以及时间轴都小于目标查询点的更改点所作出的贡献,这是一个三维偏序问题,我们在树状数组中维护x+y的最大值,进而即可更新答案。

可是这样做我们只是考虑了左下角点的贡献,肯定是会出错的,但是其余位置的点不容易化简绝对值,或者化简完以后的形式比较难以维护,而且个人认为分类的话码量略大。

那么我们换一个角度,如何将其余位置的点都变化到目标点的左下角。

直接翻转坐标系就行了,把所有点的横纵坐标都翻转一下,使之分别落于其他象限。那么再次调用cdq即可,注意防负下标。

这样打的好处就是比较无脑,而且正确性保障很大,但是常数这种东西还是很神奇的。(本代码不保证Bzoj可A)(其实应该不可A,因为main函数返回值是signed)

#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define int long long 
using namespace std;
const int inf=0x7fffffffffffff;
int read(){
    int sum=0,f=1;char x=getchar();
    while(x<'0'||x>'9'){
        if(x=='-') f=-1;
        x=getchar();
    }while(x>='0'&&x<='9'){
        sum=sum*10+x-'0';
        x=getchar();
    }return sum*f;
}
struct rec{
    int x,y,t,id,ty;
    friend bool operator < (const rec &a,const rec &b){
        if(a.t==b.t){
            if(a.x==b.x) return a.y<b.y;
            else return a.x<b.x;
        }else return a.t<b.t;
    }
}q[6005000],tmp[6005000];
int n,m,kind,Max,ans[3005000],tr[10005000],_time=1,mk[10005000];
void change(int pos,int val){
    if(!pos) return ;
    for(int i=pos;i<=Max;i+=lowbit(i))
        if(mk[i]!=_time){
            mk[i]=_time;
            tr[i]=val;
        }
        else tr[i]=max(tr[i],val);
}
int ask(int pos){
    int ans=-inf;
    for(int i=pos;i;i-=lowbit(i))
        if(mk[i]==_time) ans=max(ans,tr[i]);
    return ans;
}
void cdq(int l,int r){
    if(l==r) return ;
    int mid=l+r>>1;
    cdq(l,mid);cdq(mid+1,r);
    int i=l,j=mid+1,tot=l;
    while(i<=mid&&j<=r){
        if(q[i].x<=q[j].x){
            tmp[tot]=q[i];
            if(!q[i].ty)
                change(q[i].y,q[i].x+q[i].y);
            ++tot;++i;
        }else {
            tmp[tot]=q[j];
            if(q[j].ty)
                ans[q[j].id]=min(ans[q[j].id],q[j].x+q[j].y-ask(q[j].y));
            ++tot;++j;
        }
    }
    while(i<=mid){
        tmp[tot]=q[i];
//        if(!q[i].ty)
//            change(q[i].y,q[i].x+q[i].y);
        ++tot;++i;
    }
    while(j<=r){
        tmp[tot]=q[j];
        if(q[j].ty)
            ans[q[j].id]=min(ans[q[j].id],q[j].x+q[j].y-ask(q[j].y));
        ++tot;++j;
    }
/*    for(int i=1;i<=Max;i++)
        cout<<tr[i]<<" ";cout<<endl;*/
//    for(int k=l;k<=mid;k++) 
    //    if(!q[i].ty) del(q[k].y);
/*    for(int i=1;i<=Max;i++)
        cout<<tr[i]<<" ";cout<<endl;*/
    _time++;
//    if(_time>=10000000000) _time=1;
    for(int k=l;k<=r;k++) q[k]=tmp[k];
}
signed main(){
    n=read();m=read();
    for(int i=1;i<=n;i++){
        q[i].x=read()+1;q[i].y=read()+1;
        q[i].t=0;q[i].ty=0;
        Max=max(Max,max(q[i].x,q[i].y));
    }
    for(int i=1;i<=m;i++){
        kind=read();
        if(kind&1){
            q[i+n].x=read()+1;q[i+n].y=read()+1;
            q[i+n].t=i;q[i].ty=0;
        }else {
            q[i+n].x=read()+1;q[i+n].y=read()+1;
            q[i+n].t=i;q[i+n].ty=1;
            q[i+n].id=++ans[0];
            ans[ans[0]]=inf;
        }
        Max=max(Max,max(q[i+n].x,q[i+n].y));
    }n+=m;
//    cout<<"Max="<<Max<<endl;
    sort(q+1,q+n+1);
    cdq(1,n);
/*    for(int i=1;i<=n;i++)
        cout<<q[i].t<<" "<<q[i].x<<" "<<q[i].y<<endl;*/
    for(int i=1;i<=n;i++) q[i].x=-q[i].x+Max+1;
    sort(q+1,q+1+n);
/*    for(int i=1;i<=n;i++)
        cout<<q[i].t<<" "<<q[i].x<<" "<<q[i].y<<endl;*/
    cdq(1,n);
    for(int i=1;i<=n;i++) q[i].y=-q[i].y+Max+1;
    sort(q+1,q+1+n);
    cdq(1,n);
    for(int i=1;i<=n;i++) q[i].x=-q[i].x+Max+1;
    sort(q+1,q+n+1);
    cdq(1,n);
    for(int i=1;i<=ans[0];i++)
        printf("%lld\n",ans[i]);
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Yu-shi/p/11252540.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值