zoukankan      html  css  js  c++  java
  • cdq分治入门and持续学习orz

    感觉cdq分治是一个很有趣的算法 能将很多需要套数据结构的题通过离线来做

    目前的一些微小的理解

    在一般情况下 就像求三维偏序xyz

    就可以先对x排序 然后分治

    1 cdq_x(L,M) ;

    2 提取出(L,M)中的修改元素 作为修改操作 提取出(M+1,R)中的查询元素 作为查询操作 然后存入数组q 对q按照y排序

      这样 在q中 关于y 所有的修改操作 都会在 可能作用到的查询操作前面

      似乎可以将这一步 称作"对x维度的剥离" 在对q数组接下来的操作中 不需要考虑x维度 

    3 cdq_x(M+1,R);

    /// 迷之分析

    三维偏序 有时间和xyz
    一开始 分两半时间 左边时间一定比右边小 然后记录下左边的修改操作与右边的非修改操作
    下一步剥离 对新的区间进行sort 保证了这些修改 查询都各自有意义
    sort的是x 则下面的操作中
    * 所有的操作 对于其后的询问 都有意义
    再次剥离出y 因为之前对x进行了sort
    所以之后的新区间(只有z存在大小关系的区间)
    前面的半个区间(x层面上的) 对于后半个区间 都有明确的先后关系
    而降到z一维的时候 直接树状数组就可以
    

    ///迷之证明

    如何证明 这些修改和查询都有类似时间线的压制力
    一开始的时间可以使first唯独 不可逆
    之后的查询都有x1<x2 y1<y2 z1<z2
    故若x1>x2则没有意义
    1 先剥离出时间维度上 * 左边对且仅对右边有value的操作 即 左改右查 这时是剥离时间
    2 剥离x 这时的时间已完全剥离 因为时间影响在"左改右查"中已经去掉 所以剥离x也要"左改右查"
    3 到只有 yz 因为y已经被sort 所以直接对z使用树状数组
    

    一般需要一个离散化来做树状数组的相关处理

    注意下cdq的各个部分该放在哪里

    hdu 5324

    求最小字典序的最长偏序序列

    cdq分治 由于要记录最小字典序 所以倒着做一下 去掉时间的影响 sort y后 就可以直接x 从R->L 直接树状数组维护了

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<math.h>
    #include<map>
    #include<iostream>
    #include<string>
    #include<queue>
    #include<vector>
    using namespace std;
    #define pb push_back
    
    struct node {
        int x , y  ;
        int id ;
        int xg ;
    }a[50050];
    
    int len[50050] ;
    int nex[50050] ;
    
    int c[50050 + 500] ;
    int tid[50050 + 500] ;
    
    int n ;
    
    node q1[50050] ;
    
    bool cmpy(node a,node b) {
        if(a.y != b.y) return a.y < b.y ;
        return a.x > b.x ;
    }
    
    int lowbit(int x) {return (x & (-x)) ; } ;
    
    void cdq_x(int L , int R) {
        for(int i = R ; i >= L ; i -- ) {
            if(q1[i].xg == 1) {
                int lenn = len[q1[i].id] ;
                int nexx = nex[q1[i].id] ;
                for(int j = q1[i].x ; j <= 50050 ; j += lowbit(j)) {
                    if(c[j] < lenn) {
                        c[j] = lenn ;
                        tid[j] = q1[i].id ;
                    }
                    else if(c[j] == lenn) {
                        if(tid[j] > q1[i].id) {
                            tid[j] = q1[i].id ;
                        }
                    }
                }
            }
            else {
                int lenn = len[q1[i].id] ;
                int nexx = nex[q1[i].id] ;
                for(int j = q1[i].x ; j > 0 ; j -= lowbit(j)) {
                    if(c[j] == 0) continue ;
                    if(c[j] + 1 > lenn) {
                        len[q1[i].id] = c[j]+1 ; lenn = len[q1[i].id] ;
                        nex[q1[i].id] = tid[j] ; nexx = nex[q1[i].id] ;
                    }
                    else if(c[j] + 1 == lenn) {
                        if(tid[j] < nexx) {
                            len[q1[i].id] = c[j]+1 ; lenn = len[q1[i].id] ;
                            nex[q1[i].id] = tid[j] ; nexx = nex[q1[i].id] ;
                        }
                    }
                }
            }
        }
        for(int i = L ; i <= R ; i ++ ) {
            if(q1[i].xg == 1) {
                for(int j = q1[i].x ; j <= 50050 ; j += lowbit(j)) {
                    tid[j] = 0 ; c[j] = 0 ;
                }
            }
        }
    }
    
    void cdq_time(int L , int R) {
        if(L >= R) return ;
        int M = (L + R) / 2 ;
        cdq_time(M+1 , R) ;
        int num = L ;
        for(int i = M+1 ; i <= R ; i ++ ) {
            q1[num] = a[i] ;
            q1[num++].xg = 1 ;
        }
        for(int i = L ; i <= M ; i ++ ) {
            q1[num] = a[i] ;
            q1[num++].xg = 0 ;
        }
        sort(q1+L , q1+num , cmpy) ;
        cdq_x(L , num-1) ;
        cdq_time(L , M) ;
    }
    
    int main () {
        while(scanf("%d",&n) != EOF) {
            vector<int >ls ;
            ls.clear() ;
            for(int i=1;i<=n;i++)scanf("%d",&a[i].x) ; /// down
            for(int i=1;i<=n;i++)scanf("%d",&a[i].y) ;
            for(int i=1;i<=n;i++){
                nex[i] = i ;
                len[i] = 1 ;
                a[i].id = i ;
                ls.pb(a[i].x) ;
            }
            sort(ls.begin() , ls.end()) ;
            ls.erase(unique(ls.begin() , ls.end()) , ls.end()) ;
            for(int i = 1 ; i <= n ; i ++ ) {
                int where = lower_bound(ls.begin() , ls.end() , a[i].x) - ls.begin() + 3 ;
                a[i].x = where ;
            }
            cdq_time(1 , n) ;
            int ans = 0 ;
            int le = 0 ;
            for(int i = 1 ; i <= n ; i ++ ) {
                if(len[i] > le) {
                    le = len[i] ;
                    ans = i ;
                }
            }
            printf("%d
    ",le) ;
            printf("%d",ans) ;
            while(true) {
                if(nex[ans] == ans) break ;
                ans = nex[ans] ;
                printf(" %d",ans) ;
            }
            printf("
    ") ;
        }
    }
    

    hdu 4742

    记录最长三维偏序的方案数

    和上一个题类似 但是求得是方案 于是把与c数组并列的tid数组改为次数数组 这个正着做就可以了 

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<math.h>
    #include<map>
    #include<vector>
    using namespace std;
    
    const int mod = (1<<30) ;
    
    struct node {
        int x , y , z;
        int ansid ;
        int xg ;
    }a[100050];
    int ans[100050] ;
    int len[100050] ;
    int n ;
    
    int c[100050+500] ;
    int cishu[100050+500] ;
    
    int maxx ;
    
    node q1[100050] , q2[100050] ;
    
    bool cmpx(node a,node b) {
        if(a.x == b.x) {
            if(a.y == b.y) return a.z < b.z ;
            else return a.y < b.y ;
        }
        else return a.x < b.x ;
    }
    bool cmpy(node a,node b) {
        if(a.y == b.y) {
            return a.z < b.z ;
        }
        else return a.y < b.y ;
    }
    
    int lowbit(int x) {return (x & (-x)) ; } ;
    
    void cdq_y(int L , int R) {
        ///printf("-
    ") ;
        for(int i = L ; i <= R ; i ++ ) {
            if(q2[i].xg == 1) {
                for(int j = q2[i].z ; j <= maxx ; j += lowbit(j) ) {
                    if(c[j] < len[q2[i].ansid]) {
                        c[j] = len[q2[i].ansid ] ;
                        cishu[j] = ans[q2[i].ansid] ;
                    }
                    else if(c[j] == len[q2[i].ansid]) {
                        cishu[j] += ans[q2[i].ansid] ;
                        cishu[j] %= mod ;
                    }
    
                }
            }
            else {
                for(int j = q2[i].z ; j > 0 ; j -= lowbit(j)) {
                    if(c[j]+1 > len[q2[i].ansid]) {
                        len[q2[i].ansid] = c[j]+1 ;
                        ans[q2[i].ansid] = cishu[j] ;
                    }
                    else if(c[j] + 1 == len[q2[i].ansid]) {
                        ans[q2[i].ansid] += cishu[j] ;
                        ans[q2[i].ansid] %= mod ;
                    }
                }
            }
        }
        for(int i = L ; i <= R ; i ++ ) {
            if(q2[i].xg == 1) {
                for(int j = q2[i].z ; j <= maxx ; j += lowbit(j)) {
                    //if(c[j] == 0) continue ;
                    c[j] = 0 ;
                    cishu[j] = 0 ;
                }
            }
        }
    }
    
    void cdq_x(int L , int R) {
        if(L >= R) return ;
        int M = (L + R) / 2 ;
        cdq_x(L , M) ;
        int num = L ;
        for(int i = L ; i <= M ; i ++ ) {
            q2[num ++ ] = a[i] ;
            q2[num-1].xg = 1 ;
        }
        for(int i = M + 1 ; i <= R ; i ++ ) {
            q2[num ++ ] = a[i] ;
            q2[num-1].xg = 0 ;
        }
        sort(q2+L , q2+num , cmpy) ;
        cdq_y(L , num-1) ;
        cdq_x(M + 1 , R) ;
    }
    
    /*void cdq_time(int L , int R) {
        if(L >= R) return ;
        int M = (L + R) / 2 ;
        cdq_time(L , M) ;
        sort(a+L , a+R+1 , cmpx) ;
        cdq_x(L,R) ;
        cdq_time(M+1 , R) ;
    }*/
    
    vector<int >ls ;
    
    int main () {
        int T ;
        scanf("%d",&T) ;
        while(T--) {
            scanf("%d",&n) ;
            ls.clear() ;
            memset(c,0,sizeof(c)) ;
            memset(cishu,0,sizeof(cishu)) ;
            for(int i = 1 ; i <= n ; i ++ ) {
                ans[i] = 1 ;
                len[i] = 1 ;
                scanf("%d%d%d",&a[i].x , &a[i].y , &a[i].z) ;
                a[i].ansid = i ;
                ls.push_back(a[i].z) ;
            }
            sort(ls.begin() , ls.end()) ;
            ls.erase(unique(ls.begin() , ls.end()) , ls.end()) ;
            maxx = ls.size() + 20 ;
            for(int i = 1 ; i <= n ; i ++ ) {
                int where = lower_bound(ls.begin() , ls.end() , a[i].z) - ls.begin() + 5 ;
                a[i].z = where ;
            }
            sort(a+1,a+1+n,cmpx) ;
            cdq_x(1 , n) ;
            int realans = 0 ;
            int realcishu = 0 ;
            for(int i = 1 ; i <= n ; i ++ ) {
                if(len[i] > realans) {
                    realcishu = ans[i] ;
                    realans = len[i] ;
                }
                else if(len[i] == realans) {
                    realcishu += ans[i] ;
                    realcishu %= mod ;
                }
            }
            printf("%d %d
    ",realans , realcishu) ;
        }
    }
    

    hdu 5126 & bzoj 1935

    动态的三维和二维空间询问有多少点

    其实是可以直接kd上的...但是也可以cdq 并且省去了写数据结构的麻烦

    其实就是把 一个空间的询问 拆分成多个空间->原点的询问 这个就可以用树状数组做一个前缀和的处理 并且记录下每个询问的ansid 随时更新

    一个询问操作当然可以被查询很多次 但是可以看出 因为二分 每个之前的修改操作都只会在一个区域被访问一次

    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<math.h>
    #include<map>
    #include<iostream>
    #include<string>
    #include<queue>
    #include<vector>
    using namespace std;
    #define pb push_back
    
    int cnt ;
    
    int maxx ;
    
    struct node {
        int x , y , z ;
        int idx ;
        int tp ;
    
    }a[50050 * 10] , q1[50000 * 10] , q2[50000 * 10];
    
    int c[500500] ;
    int lowbit(int x) {return (x&(-x)) ; }
    void add(int x , int val) {
        while(x <= maxx + 20) {
            c[x] += val ;
            x += lowbit(x) ;
        }
    }
    int sum(int x) {
        int res = 0 ;
        while(x) {
            res += c[x] ; x -= lowbit(x) ;
        }
        return res ;
    }
    
    int n ;
    
    int ans[50050] ;
    
    struct ls{
        int x , id;
        int idx ;
    }lss[50050 * 10];
    
    bool cmpls(ls a , ls b) {
        return a.x < b.x ;
    }
    bool cmpx(node a,node b) {
        if(a.x == b.x) return a.tp < b.tp ;
        else return a.x < b.x ;
    }
    bool cmpy(node a,node b) {
        if(a.y == b.y) return a.tp < b.tp ;
        else return a.y < b.y ;
    }
    
    void cdq_y(int L , int R) { /// 其实这个时候 y 已经被排好序了 所以直接按照z 进行树状数组就可以了 这时候的z已经离散化了
        for(int i = L ; i <= R ; i ++ ){
            if(q2[i].tp == 0) {
                add(q2[i].z , 1) ;
            }
            else {
                int res = sum(q2[i].z) ;
                if(q2[i].tp == 1) {
                    ans[q2[i].idx] += res ;
                }
                else {
                    ans[q2[i].idx] -= res ;
                }
            }
        }
        for(int i = L ; i <= R ; i ++ ) {
            if(q2[i].tp == 0) {
                add(q2[i].z , -1) ;
            }
        }
    }
    
    void cdq_x(int L , int R) { /// 只要还没有剥离到可以直接利用数据结构得出答案 就应该一直分治
        if(L >= R) return ;
        int M = (L + R) / 2 ;
        cdq_x(L , M) ; cdq_x(M+1 , R) ;
        int num = L ;
        for(int i = L ; i <= M ; i ++ ) if(q1[i].tp == 0) q2[num ++ ] = q1[i] ;
        for(int i = M + 1 ; i <= R ; i ++ ) if(q1[i].tp != 0) q2[num ++ ] = q1[i] ;
        sort(q2 + L , q2 + num , cmpy) ;
        cdq_y(L , num-1) ;
    }
    
    void cdq_time(int L , int R) {
        if(L >= R) return ;
        int M = (L + R) / 2 ;
        cdq_time(L , M) ; cdq_time(M+1 , R) ;
        int num = L ;
        for(int i = L ; i <= M ; i ++ ) if(a[i].tp == 0) q1[num ++ ] = a[i] ;
        for(int i = M + 1 ; i <= R ; i ++ ) if(a[i].tp != 0) q1[num ++ ] = a[i] ;
        sort(q1 + L , q1 + num , cmpx) ;  /// 上面两行 其实就已经剥离出时间了 所以这个时候应该按照x排序 便于剥离x
        cdq_x(L , num-1) ;
    }
    
    int main (){
        int T;
        scanf("%d" , &T) ;
        while(T -- ) {
            memset(c , 0 , sizeof(c)) ;
            scanf("%d" , &n) ;
            memset(ans , 0 , sizeof(ans)) ;
            cnt = 0 ;
            int now = 0 ;
            for(int i = 1 ; i <= n ; i ++ ) {
                int tp ;
                scanf("%d" , &tp) ;
                if(tp == 1) {
                    int x,y,z;
                    scanf("%d%d%d" , &x , &y , &z) ;
                    cnt ++ ;
                    a[cnt].tp = 0 ;
                    a[cnt].x = x ; a[cnt].y = y ; a[cnt].z = z ;
                    a[cnt].idx = i ;
                    ans[i] = -1 ;
                    now ++ ;
                    lss[now].id = cnt ;
                    lss[now].x = z ;
                }
                else {
                    int x,y,z,x2,y2,z2 ;
                    scanf("%d%d%d%d%d%d" , &x , &y , &z , &x2 , &y2 , &z2) ;
                    cnt ++ ; a[cnt] = {x2 , y2 , z2 , i , 1} ;
                    now ++ ; lss[now].id = cnt ; lss[now].x = z2 ;
    
                    cnt ++ ; a[cnt] = {x-1 , y2 , z2 , i , 2} ;
                    now ++ ; lss[now].id = cnt ; lss[now].x = z2 ;
    
                    cnt ++ ; a[cnt] = {x2 , y-1 , z2 , i , 2} ;
                    now ++ ; lss[now].id = cnt ; lss[now].x = z2 ;
    
                    cnt ++ ; a[cnt] = {x2 , y2 , z-1 , i , 2} ;
                    now ++ ; lss[now].id = cnt ; lss[now].x = z-1 ;
    
                    cnt ++ ; a[cnt] = {x-1 , y-1 , z2 , i , 1} ;
                    now ++ ; lss[now].id = cnt ; lss[now].x = z2 ;
    
                    cnt ++ ; a[cnt] = {x-1 , y2 , z-1 , i , 1} ;
                    now ++ ; lss[now].id = cnt ; lss[now].x = z-1 ;
    
                    cnt ++ ; a[cnt] = {x2 , y-1 , z-1 , i , 1} ;
                    now ++ ; lss[now].id = cnt ; lss[now].x = z-1 ;
    
                    cnt ++ ; a[cnt] = {x-1 , y-1 , z-1 , i , 2} ;
                    now ++ ; lss[now].id = cnt ; lss[now].x = z-1 ;
                }
            }
            sort(lss+1 , lss+1+now , cmpls) ;
            int res = 0 ;
            lss[0].x = -97989 ;
            for(int i=1;i<=now;i++) {
                if(lss[i].x == lss[i-1].x) {
                    a[lss[i].id].z = lss[i-1].idx;
                    lss[i].idx = lss[i-1].idx;
                }
                else {
                    res ++ ;
                    lss[i].idx = res ;
                    a[lss[i].id].z = res ;
                }
            }
            maxx = res ;
            cdq_time(1 , cnt) ;
            for(int i = 1 ; i <= n ; i ++ ) {
                if(ans[i] != -1) printf("%d
    ",ans[i]) ;
            }
        }
    }
    

    虽然..看起来还是挺长的...

    被打哭 意识到自己其实还不会cdq 于是再次学习

    发现过去写的 逐渐剥离出维度 有点暴力了

    算法的正确性是没有问题的 但是每次都sort 会多一个log 可以模仿归并排序

    如果我先进行了cdq(L,M) cdq(M+1,R) 那么两边的数组都是排序好了的 就可以双指针排序这个数组 省去一个sort的时间

    bzoj1935

    #include<stdio.h>
    #include<math.h>
    #include<string.h>
    #include<vector>
    #include<queue>
    #include<map>
    #include<string>
    #include<iostream>
    #include<algorithm>
    #include<stack>
    using namespace std;
    #define L long long
    #define pb push_back
    #define lala printf("--------
    ");
    #define ph push
    #define rep(i, a, b) for (int i=a;i<=b;++i)
    #define dow(i, b, a) for (int i=b;i>=a;--i)
    #define fmt(i,n) if(i==n)printf("
    ");else printf(" ") ;
    #define fi first
    #define se second
    template<class T> inline void flc(T &A, int x){memset(A, x, sizeof(A));}
    int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
    
    int num;
    struct node {
        int x,y;
        int time ;
        int xw;
        int id;
    }q[500050 * 5],tmp[500050 * 5];
    int c[10000050] ;
    int lowbit(int x){return (x&(-x));}
    void add(int x,int val) {while(x<=10000000) {c[x]+=val;x+=lowbit(x);}}
    int sum(int x) {int res = 0 ; while(x) {res+=c[x];x-=lowbit(x);} return res ;}
    int n , m ;
    int ans[500050] ;
    bool cmp(node a, node b) {
        if(a.x == b.x) return a.time < b.time ;
        return a.x < b.x ;
    }
    void cdq(int LL , int RR) {
        if(RR-LL <= 0) return ;
        int M = (LL+RR)/2 ;
        int pl = LL ;
        int pr = M+1 ;
        int pp = LL ;
        cdq(LL , M) ; cdq(M+1,RR) ;  /// 在这里 先对两边进行递归 这样可以模仿一下归并排序 省去一个sort x的复杂度
        while(pl <= M && pr <= RR) {
            if(q[pl].x < q[pr].x || (q[pl].x == q[pr].x && q[pl].time < q[pr].time)) { /// 还是一个归并排序 左边的时间一定早于右边的时间 所以左边的修改可能对右边的查询有影响 但是不一定
                                     /// 但是由于两边的x其实都已经归并排序好了 所以模仿归并的过程 面对的实际上是一个sort好的数组
                if(q[pl].xw == 0) {
                        add(q[pl].y , 1) ;
                }
                tmp[pp ++ ] = q[pl ++ ] ;
            }
            else {
                if(q[pr].xw == 1) {
                    ans[q[pr].id] += sum(q[pr].y) ;
                }
                else if(q[pr].xw == 2) {
                    ans[q[pr].id] -= sum(q[pr].y) ;
                }
                tmp[pp ++ ] = q[pr ++ ] ;
            }
        }
        while(pl <= M) {
            if(q[pl].xw == 0) add(q[pl].y , 1) ;
            tmp[pp ++ ] = q[pl ++ ] ;
        }
        while(pr <= RR) {
            if(q[pr].xw == 1)
                ans[q[pr].id] += sum(q[pr].y) ;
            else if(q[pr].xw == 2)
                ans[q[pr].id] -= sum(q[pr].y) ;
            tmp[pp ++ ] = q[pr ++ ] ;
        }
        for(int i = LL ; i <= M ; i ++ ) { /// 得消除掉c的操作 保持为0
            if(q[i].xw == 0)
                add(q[i].y , -1) ;
    
        }
        for(int i = LL ; i <= RR ; i ++ ) q[i] = tmp[i] ; /// 归并排序 数组复制
    }
    
    int main () {
        n=read();m=read();
        flc(c,0);
        num=0;
        rep(i,1,n) {
            int x=read();int y=read();
            x ++ ; y ++ ;
            num++;
            q[num]={x,y,num,0,0} ;
        }
        rep(i,1,m) {
            int x1=read();int y1=read();int x2=read();int y2=read();
            x1 ++ ; y1 ++ ; x2 ++ ; y2 ++ ;
            q[++num]={x2,y2,num,1,i};
            q[++num]={x1-1,y1-1,num,1,i};
            q[++num]={x1-1,y2,num,2,i};
            q[++num]={x2,y1-1,num,2,i};
        }
        flc(ans,0);
        cdq(1,num) ;
        rep(i,1,m) {
            printf("%d
    " , ans[i]) ;
        }
    }
    

     cdq的复杂度是每层一个logn 如果直接sort的话其实也是这个复杂度 归并排序会省去一个常数

  • 相关阅读:
    CentOS最常用命令
    cssreset
    mysql cmd连接 乱码
    帮朋友转发招聘信息 南京知名互联网公司招聘java、测试、前端,具体私聊
    InputStream写文件出现大量NUL
    iOS app开发资料整理
    完美的视图旋转算法
    Windows上使用clang编译
    nodejs
    Rpath handling on Linux
  • 原文地址:https://www.cnblogs.com/rayrayrainrain/p/6686672.html
Copyright © 2011-2022 走看看