• cdq分治


    cdq分治用来解决多维偏序问题,分治时统计左区间的修改对右区间产生的影响
    之所以不考虑右区间对左区间的影响,是因为通常已经通过排序消掉了一维,右区间对左区间不会产生影响
    cdq分治是一种离线算法
    二维偏序
    将其中一维排序,消掉一维的影响,另一维通过cdq分治处理

    1.逆序对问题
    计算数列中的逆序对个数
    逆序对是指(i<j)并且(a_i>a_j)的数对,逆序对可以在归并排序的合并过程中计算,下标所在的维度是默认有序的,如果合并时左区间中的(a[i])大于右区间中的(a[j]),则对于下标为(j)的元素来说,左区间对它产生的影响是逆序对数增加((mid-i+1))

    const int maxn=100010;
    int n,k,a[maxn],b[maxn];
    LL cnt;
    
    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,k=l;
        while(i<=mid && j<=r){
            if(a[i]<=a[j]) b[k++]=a[i++];
            else{
                cnt+=mid-i+1;
                b[k++]=a[j++];
            }
        }
        while(i<=mid) b[k++]=a[i++];
        while(j<=r) b[k++]=a[j++];
        for(i=l;i<=r;i++) a[i]=b[i];
    }
    

    2.单点修改,区间查询问题
    设对于一个数列有两种操作:
    1 x y 表示将下标为x的元素加上y
    2 x y 表示计算区间[x,y]之内的元素和

    树状数组单点修改,区间求和模板,也可以通过cdq分治处理
    设二维偏序(a,b),其中a是操作时间,b是操作位置
    时间作为默认有序的第一维度,就是将所有操作按照进行的顺序作为数组下标,cdq分治处理第二维度
    将每个查询操作拆分成两个元素:l-1的前缀和以及r的前缀和
    每个元素包含三个变量:
    type:操作类型,1表示修改,2表示查询l-1的前缀和,3表示查询r的前缀和
    pos:操作位置
    val:修改操作表示增加的值,查询操作表示查询编号

    const int maxn=50010,maxm=50010;
    int n,m,ans[maxm];
    struct node{
        int type,pos,val;
        bool operator < (const struct node &t)const{
            if(pos!=t.pos) return pos<t.pos;
            return type<t.type;
        }
    }a[2*maxm+maxn],b[2*maxm+maxn];
    
    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;
        LL sum=0;
        for(int k=l;k<=r;k++){
            if((i<=mid && j<=r && a[i]<a[j]) || j>r){
                if(a[i].type==1){
                    sum+=a[i].val;
                }
                b[k]=a[i++];
            }
            else{
                if(a[j].type==2){
                    ans[a[j].val]-=sum;
                }
                else if(a[j].type==3){
                    ans[a[j].val]+=sum;
                }
                b[k]=a[j++];
            }
        }
        for(int k=l;k<=r;k++) a[k]=b[k];
    }
    
    void solve(){
        scanf("%d %d",&n,&m);
        int tot=1,totq=1;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[tot].val);
            a[tot].pos=i;
            a[tot++].type=1;
        }
        for(int i=1;i<=m;i++){
            int type;
            scanf("%d",&type);
            if(type==1){
                int pos,val;
                scanf("%d %d",&pos,&val);
                a[tot].pos=pos;
                a[tot].val=val;
                a[tot++].type=1;
            }
            else{
                int l,r;
                scanf("%d %d",&l,&r);
                a[tot].pos=l-1;
                a[tot].val=totq;
                a[tot++].type=2;
                a[tot].pos=r;
                a[tot].val=totq++;
                a[tot++].type=3;
            }
        }
        cdq(1,tot-1);
        for(int i=1;i<totq;i++) printf("%d
    ",ans[i]);
    }
    

    三维偏序
    给出一些三维坐标系下点的坐标(x,y,z),计算对于每一个点(i),满足(x_igeq x_j)并且(y_igeq y_j)并且(z_igeq z_j)的点(j)的个数

    通过按照(x)值排序减掉一维,cdq分治减掉第二维,树状数组统计符合条件的第三维的点
    在cdq分治的合并过程中,按照(y)值从小到大排序,这样在统计左区间对右区间的贡献时,从左到右扫描右区间,左区间从最左端开始向右走,不回溯,将左区间中(y)值小于等于当前右区间的点的(z)值直接加入树状数组,每次统计树状数组中小于等于当前右区间中点的(z)值的点的个数。所有右区间中的点都统计完成之后,将树状数组中的所有的(z)值清空
    由于所有点初始都是按(x)值排序的,所以左区间中点的(x)值一定符合条件,而合并的时候又按照(y)值排序,所以左区间中所有(y)值符合条件的点都已经被扫描,所以直接计算树状数组中符合条件的(z)值的个数,就是满足条件的左区间中的点的个数

    模板题:hdu5618 Jam's problem again

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cstring>
    #include<string>
    #include<sstream>
    #include<cmath>
    #include<ctime>
    #include<algorithm>
    #define LL long long
    #define PII pair<int,int>
    #define PLL pair<LL,LL>
    #define pi acos(-1.0)
    #define eps 1e-6
    #define lowbit(x) x&(-x)
    using namespace std;
    
    const int maxn=100010;
    int T,n,bit[maxn],ans[maxn];
    
    struct Point{
        int x,y,z,id,sum;
        Point(){}
        Point(int x,int y):x(x),y(y){}
        bool operator < (const Point &t)const{
            if(x!=t.x) return x<t.x;
            if(y!=t.y) return y<t.y;
            return z<t.z;
        }
        bool operator == (const Point &t)const{
            return x==t.x && y==t.y && z==t.z;
        }
    }a[maxn],b[maxn];
    
    void change(int x,int v){
        while(x<=100000){
            bit[x]+=v;
            x+=lowbit(x);
        }
    }
    
    int query(int x){
        int sum=0;
        while(x){
            sum+=bit[x];
            x-=lowbit(x);
        }
        return sum;
    }
    
    void cdq(int l,int r){
        if(l==r) return;
        int mid=(l+r)>>1;
        cdq(l,mid);
        cdq(mid+1,r);
        int j=l;
        for(int i=mid+1;i<=r;i++){
            for(;j<=mid && a[j].y<=a[i].y;j++) change(a[j].z,1);
            a[i].sum+=query(a[i].z);
        }
        for(int i=l;i<j;i++) change(a[i].z,-1);
        int i=l;
        j=mid+1;
        for(int k=l;k<=r;k++){
            if(i>mid) b[k]=a[j++];
            else if(j>r) b[k]=a[i++];
            else if(a[i].y<a[j].y) b[k]=a[i++];
            else b[k]=a[j++];
        }
        for(int k=l;k<=r;k++) a[k]=b[k];
    }
    
    int main(){
        scanf("%d",&T);
        while(T--){
            scanf("%d",&n);
            for(int i=1;i<=n;i++){
                scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].z);
                a[i].id=i;
                a[i].sum=0;
            }
            sort(a+1,a+1+n);
            memset(bit,0,sizeof(bit));
            cdq(1,n);
            sort(a+1,a+1+n);
            for(int i=1;i<=n;){
                int j=i+1;
                int tmp=a[i].sum;
                for(;j<=n && a[i]==a[j];j++) tmp=max(tmp,a[j].sum);
                for(int k=i;k<j;k++) ans[a[k].id]=tmp;
                i=j;
            }
            for(int i=1;i<=n;i++){
                printf("%d
    ",ans[i]);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    VBA中的ColorIndex信息
    登录测试页面
    HttpHandler HttpModule入门篇
    vs 2005的条件断点(调试多线程必会)
    VBA中操作Excel的部分方法代码示例
    c# 线程同步: 详解lock,monitor,同步事件和等待句柄以及mutex
    一个对Entity Framework数据层的封装
    中华人民共和国 第二代身份证 号码规则
    什么是.NET应用程序域
    VBA编程常用语句(转载)
  • 原文地址:https://www.cnblogs.com/fxq1304/p/13556119.html
Copyright © 2020-2023  润新知