• 《算法竞赛进阶指南》0x47离线分治算法 CDQ分治


    题目链接:https://www.acwing.com/problem/content/256/

    题目给出n个初始点,m个操作,可能是加上一个新的点,也可能是给出一个点,查询离他曼哈顿距离最近的点的距离。由于是动态问题,有查询与改动是无序的,所以考虑使用基于时间的离线分治算法CDQ分治。该算法在递归计算左右的情况之后只需要计算左半边的更新对右半边的查询的影响。故变成了一个静态的问题,这时候将曼哈顿距离展开成四个查询即可,由于状态控制有两个,通过排序解决一个状态控制,另一个可以通过树状数组查询前缀最大值解决。树状数组每次进行calc之后都要回到原状态,不能每次都开一个树状数组。(实际上AcWing就过了17/20的数据,人没了)

    代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn = 1000010;
    const int inf=(1<<30);
    struct rec{int x,y,z;};
    rec a[maxn];//操作序列
    rec b[maxn];//静态问题的坐标,及其下标
    int c[maxn],tot;//树状数组
    int ans[maxn],n,m,t;
    inline bool operator < (const rec& a,const rec& b){
        return a.x<b.x || (a.x==b.x && a.y<b.y);
    }
    inline int read(){
        int ans=0,w=1;
        char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
        while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
        return ans*w;
    }
    //查询前缀最大值 
    inline int ask(int x){
        int ans=-inf;
        while(x){
            ans=max(ans,c[x]);
            x-=x&(-x);
        }
        return ans;
    } 
    //维护前缀最大值 
    inline void update(int x,int y){
        while(x<tot){
            c[x]=max(c[x],y);
            x+=x&(-x); 
        }
    }
    inline void calc(int st,int ed,int de,int dx,int dy){
        for(int i=st;i!=ed;i+=de){
            int y=(dy==1)?b[i].y:tot-b[i].y;//查询的是前缀 
            int tmp=dx*b[i].x+dy*b[i].y;
            if(a[b[i].z].z==1)update(y,tmp);
            else ans[b[i].z]=min(ans[b[i].z],tmp-ask(y));
        }
        for(int i=st;i!=ed;i+=de){
            int y=(dy==1)?b[i].y:tot-b[i].y;
            if(a[b[i].z].z==1){//如果是一个更新就将树状数组更新回原状态 
                for(int j=y;j<tot;j+=j&(-j))c[j]=-inf; 
            }    
        }
    }
    inline void cdqdiv(int l,int r){
        int mid=(l+r)>>1;
        if(l<mid)cdqdiv(l,mid);
        if(mid+1<r)cdqdiv(mid+1,r);
        t=0;//前半段的修改数量+后半段的询问数量
        for(int i=l;i<=r;i++){
            if(i<=mid && a[i].z==1 || i>mid && a[i].z==2)
                b[++t]=a[i],b[t].z=i;//保存下标,用来更新ans 
        } 
        sort(b+1,b+t+1); 
        calc(1,t+1,1,1,1);//计算[l,mid]段的修改对[mid+1,r]段的查询的贡献 
        calc(1,t+1,1,1,-1);
        calc(t,0,-1,-1,-1);
        calc(t,0,-1,-1,1);
    }
    int main(){
        cin>>n>>m;
        m+=n;
        //把初始的点也认为是一种加点的操作 
        //由于树状数组中下标是从1开始的,所以y++ 
        for(int i=1;i<=n;i++)
            a[i].x=read(),a[i].y=read(),a[i].y++,a[i].z=1;
        for(int i=n+1;i<=m;i++)
            a[i].z=read(),a[i].x=read(),a[i].y=read(),a[i].y++;
        
        for(int i=1;i<=m;i++)tot=max(tot,a[i].y);//树状数组下标的最大值
        tot++;
        memset(ans,0x3f,sizeof(ans));
        memset(c,0xcf,sizeof(c));//赋值,一个绝对值极大的负数
        
        cdqdiv(1,m);
        for(int i=1;i<=m;i++)
            if(a[i].z==2)printf("%d
    ",ans[i]); 
    } 
    每一个不曾起舞的日子,都是对生命的辜负。
  • 相关阅读:
    使用OPC的方式去连接PLC进行AB SLC-5_04数据的采集
    pytest:conftest.py运行细节一则
    《数据结构与算法》和《设计模式》之开门见山篇
    C语言真正的编译过程
    外挂原理之植物大战僵尸
    ORACLE数据库创建动态表
    JS中事件绑定问题
    由 “无法使用从远程表选择的 lob 定位符” 错误而引导出来的一系列问题解决方案
    安装JDK,配置环境变量有感
    MSSQL 常见故障处理
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13359581.html
Copyright © 2020-2023  润新知