zoukankan      html  css  js  c++  java
  • cdq分治

    初略写一下cdq分治,想要更详细的可以去看别人的更优质的博客。

    cdq分治的本质是用到了归并排序的,

    所以不知道归并排序怎么打的人可以先去学归并排序,

    先上三维偏序的cdq分治代码..  模板题传送门

    #include<bits/stdc++.h>
    #define lowbits(i) i&(-i) 
    using namespace std;
    const int N=1e5+1e4;
    int n,k,m,b[N*4],c[N];
    struct PP{
        int a,b,c,w,ans;
    }a[N],tmp[N];
    int rd(){
        int s=0,ff=1;
        char w=getchar();
        while(w<'0'||w>'9'){
            if(w=='-') ff=-1;
            w=getchar();
        }
        while(w>='0'&&w<='9'){
            s=s*10+(w-'0');
            w=getchar();
        }
        return s*ff;
    }
    bool cmp(PP aa,PP bb){
        return aa.a==bb.a?(aa.b==bb.b?aa.c<bb.c:aa.b<bb.b):aa.a<bb.a;
    }
    void Add(int x,int y){
        for(int i=x;i<=k;i+=lowbits(i)){
            b[i]+=y;
        }
    }
    void Del(int x){
        for(int i=x;i<=k;i+=lowbits(i)){
            b[i]=0;
        }
    }
    int Query(int x){
        int sum=0;
        for(int i=x;i>0;i-=lowbits(i)){    
            sum+=b[i];
        }
        return sum;
    }
    bool operator<(PP aa,PP bb){
        return aa.b==bb.b?aa.c<=bb.c:aa.b<bb.b;
    }
    void Cdq(int l,int r){    
        if(l==r){
            return ;
        }
        int mid=(l+r)>>1;
        Cdq(l,mid); Cdq(mid+1,r);
        int t=l,L=l,M=mid+1;
        while(L<=mid&&M<=r){
            if(a[L]<a[M]){
                Add(a[L].c,a[L].w);
                tmp[t++]=a[L++];
            }
            else{
                a[M].ans+=Query(a[M].c);
                tmp[t++]=a[M++];
            }
        }
        while(L<=mid){
            Add(a[L].c,a[L].w);
            tmp[t++]=a[L++];
        }
        while(M<=r){
            a[M].ans+=Query(a[M].c);
            tmp[t++]=a[M++];
        }
        for(int i=l;i<=r;i++){
            a[i]=tmp[i];
            Del(a[i].c);
        }
    }
    int main(){
        int minn=1e9; m=rd(); k=rd();
        for(int i=1;i<=m;i++){
            a[i]=(PP){rd(),rd(),rd(),1,0};
            minn=min(minn,a[i].c);
        } minn--; k-=minn;
        for(int i=1;i<=m;i++){
            a[i].c-=minn;
        }
        sort(a+1,a+1+m,cmp);
        for(int i=1;i<=m;i++){
            if(a[i].a==a[i-1].a&&a[i].b==a[i-1].b&&a[i].c==a[i-1].c){
                a[i].w=a[i-1].w+1; a[i-1]=(PP){1e9,1e9,1e9,0,0};
            }
        } sort(a+1,a+1+m,cmp); n=m;
        while(!a[n].w) {n--;} Cdq(1,n);
        for(int i=1;i<=n;i++){
            c[a[i].ans+a[i].w-1]+=a[i].w;
        }
        for(int i=0;i<m;i++){
            printf("%d
    ",c[i]);
        }
        return 0;
    }

    先解释时间复杂度

    因为cdq分治的过程是在归并排序中进行的,

    所以cdq分治的时间复杂度为O(nlog n),

    但是这题(三维偏序)需要用到sort+树状数组+cdq分治,

    所以时间复杂度为O(nlog n log k)(k为最大元素值)。

    这题也可以用sort+cdq分治+cdq分治,

    时间复杂度为O(nlog2n)。

    下面是原理

    用二维偏序解释吧,这样好解释点,

    (第一维是a[],第二维是b[],

    求f[i]为a[i]>=a[j]&&b[i]>=b[j]的j的个数)

    当然二维偏序可以用(sort+树状数组)过,

    但是我们就是要用(sort+cdq分治)过,

    首先用sort解决一维,使其单调排序,

    然后第二维的话如图所示(图中视为第一维已经排好序了):

    先是归并排序向下递归

    再是归并排序向上合并(左边的指针为i,右边的指针为j)

    就是在合并的时候要统计左边一块对右边一块的答案贡献,

    因为左边的a是小于等于右边的a的,所以不管,

    当指针i所在的数小于等于指针j所在的数,

    说明在j之后的数的答案贡献都可以加上1,

    用一个变量累加存过去就好了,

    当然你得存下一个数一开始在数列中的位置,

    代码如下:

    void Cdq(int l,int r){
        if(l==r){
            return ;
        }
        int mid=(l+r)>>1;
        Cdq(l,mid); Cdq(mid+1,r);
        int t=l,i=l,j=mid+1,ss=0;
        while(i<=mid&&j<=r){
            if(b[i]<=b[j]){
                ss++,tmp[t++]=b[i++];
            }
            else{
                f[w[b[j]]]+=ss,tmp[t++]=b[j++];//w[b[j]]表示b[j]这个数在数列中的位置;
            }
        }
        while(i<=mid){
            ss++,tmp[t++]=b[i++];
        }
        while(j<=r){
            f[w[b[j]]]+=ss,tmp[t++]=b[j++];
        }
        for(int i=l;i<=r;i++){
            b[i]=tmp[i];
        }
    }

    所以cdq分治主要的思想就是:

    分治->合并->算左边对右边的贡献

    最后再强调下单用cdq分治时间复杂度是O(nlog n)的,

    三维偏序中再加上树状数组才是O(nlog n log k)的。

    就写到这了。哪里不明白可以评论问我。

  • 相关阅读:
    Bufferedreader和BufferedWriter(带缓存的输入输出)
    FileReader和FileWriter
    Map接口的类实现
    Map接口
    Set集合
    List接口的实现类
    获取文本框或密码框中的内容
    ADTS (zz)
    初级美语 L003:My Family 解析
    初级美语 L001:Self Introduction 解析
  • 原文地址:https://www.cnblogs.com/manmanjiangQwQ/p/11239567.html
Copyright © 2011-2022 走看看