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

    CDQ分治是种离线的分治算法

    通常解决带有修改和问询且不强制在线的一类问题

    本人菜鸡,就只能推推文章这样子 ==> CDQ分治

    说一些点

    ① CDQ的优点就是能起到降维的作用,从而顶替了本应多套一层数据结构才能维护的信息

    具体点来说就是“消除了某一维”的影响、例如在解决三维偏序的过程中,先根据a、b、c排序

    分治时根据 b 排序,这时各个区间的 a 元素可能因为 b 排序导致乱序,但左边区间的元素总

    是比右边区间小,所以若是考虑左边区间对右边区间的贡献的话,则可以忽略 a 的影响,即

    虑的对象变成了区间、而不是具体的 a 了,所以相当于降了一个维度。此时左右区间中只有

    a、b 可以判断满足偏序关系还是不够的,还有一维 c ,这个时候用树状数组维护第三维即可

    ② 根据 ① 的解释,所以在 CDQ 分治之前,需要排序且去重,使得满足左边区间总比右边

    区间小。而在通常的拓展问题中,即一些带有修改和问询的操作中,通常有时间作为一个维

    度的话,便不用排序和去重,因为时间默认有序、且时间这一维肯定不会重复!而普通的三维

    偏序问题中,就需要排序一下再进行 CDQ 分治

    一些题目 :

    二维偏序 ==> 即归并排序求解逆序对 (数组下标、数组元素值) 这样的二元组

    二维偏序拓展 ==> 单点修改、区间求和  洛谷 3374 

    #include<bits/stdc++.h>
    #define LL long long
    #define pb(i) push_back(i)
    #define mem(i, j) memset(i, j, sizeof(i))
    using namespace std;
    const int maxn = 5e5 + 10;///原序列大小
    const int maxm = 5e5 + 10;///操作的数量
    const int maxQ = (maxm<<1)+maxn;///数组大小 ==> 询问被分成两次前缀问询、初始元素被分成一次插入操作
    struct Query{
        int type, pos; LL val;
        bool operator < (const Query &rhs) const{///对于同一位置的、修改操作优先级高于问询
            if(this->pos == rhs.pos) return this->type < rhs.type;
            else return this->pos < rhs.pos;
        };
    }query[maxQ];
    
    int qnum = 0/*问询的数量*/, n, m;
    LL ans[maxQ]; int anum = 0;/*答案数组、答案数组的长度*/
    
    Query tmp[maxQ];
    void CDQ(int L, int R)
    {
        if(R-L <= 1) return ;
    
        int M = (R+L)>>1;
        CDQ(L, M);
        CDQ(M, R);
    
        LL PreSum = 0;
        int l = L, r = M, idx = 0;
        while(l < M && r < R){
            if(query[l] < query[r]){
                if(query[l].type == 1) PreSum += query[l].val;
                tmp[idx++] = query[l++];
            }else{
                if(query[r].type == 2) ans[query[r].val] -= PreSum;
                else if(query[r].type == 3) ans[query[r].val] += PreSum;
                tmp[idx++] = query[r++];
            }
        }
    
        while(l < M) tmp[idx++] = query[l++];
        while(r < R){
            if(query[r].type == 2) ans[query[r].val] -= PreSum;
            else if(query[r].type == 3) ans[query[r].val] += PreSum;
            tmp[idx++] = query[r++];
        }
    
        for(int i=0; i<idx; i++) query[i+L] = tmp[i];
    }
    
    
    int main(void)
    {
        scanf("%d %d", &n, &m);
        for(int i=1; i<=n; i++){
            query[qnum].pos = i;
            query[qnum].type = 1;
            scanf("%lld", &query[qnum].val);
            qnum++;
        }
    
        for(int i=0; i<m; i++){
            int type;
            scanf("%d", &type);
            query[qnum].type = type;
            if(type == 1) scanf("%d %lld", &query[qnum].pos, &query[qnum].val);
            else{
                int L, R;
                scanf("%d %d", &L, &R);
    
                query[qnum].pos = L-1;
                query[qnum].type = 2;
                query[qnum].val = anum;
                qnum++;
    
                query[qnum].pos = R;
                query[qnum].type = 3;
                query[qnum].val = anum;
                anum++;
            }
            qnum++;
        }
    
        CDQ(0, qnum);
    
        for(int i=0; i<anum; i++) printf("%lld
    ", ans[i]);
    
        return 0;
    }
    View Code

    三维排序 ==>  LOJ 三维偏序

    #include<bits/stdc++.h>
    #define mem(i, j) memset(i, j, sizeof(i))
    #define lowbit(i) (i & (-i))
    using namespace std;
    const int maxn = 1e5 + 10;
    const int maxm = 2e5 + 10;
    struct ARRAY{ int a, b, c, num, res; }arr[maxn], tmp[maxn];
    int N, K, cnt[maxn], ans[maxn];
    
    int c[maxm];
    
    inline void add(int i, int val)
    {
        while(i <= K){
            c[i] += val;
            i += lowbit(i);
        }
    }
    
    inline void Clear(int i)
    {
        while(i <= K){
            c[i] = 0;
            i += lowbit(i);
        }
    }
    
    int sum(int i)
    {
        int ret = 0;
        while(i > 0){
            ret += c[i];
            i -= lowbit(i);
        }return ret;
    }
    
    bool cmp(const ARRAY& fir, const ARRAY& sec){
        if(fir.a == sec.a){
            if(fir.b == sec.b){
                return fir.c < sec.c;
            }return fir.b < sec.b;
        }return fir.a < sec.a;
    }
    
    void CDQ(int L, int R)
    {
        if(R - L <= 1) return;
        int M = L + ((R-L)>>1);
        CDQ(L, M);
        CDQ(M, R);
    
        int l = L, r = M, idx = 0;
        while(l < M && r < R){
            if( (arr[l].b == arr[r].b) ?
                (arr[l].c <= arr[r].c) :
                (arr[l].b  < arr[r].b) ) { add(arr[l].c, arr[l].num); tmp[idx++] = arr[l++]; }
            else { arr[r].res += sum(arr[r].c); tmp[idx++] = arr[r++]; }
        }
    
        while(l < M) tmp[idx++] = arr[l++];
        while(r < R) { arr[r].res += sum(arr[r].c); tmp[idx++] = arr[r++]; }
    
        for(int i=0; i<idx; i++){
            arr[i+L] = tmp[i];
            Clear(tmp[i].c);
        }
    }
    
    int main(void)
    {
        scanf("%d %d", &N, &K);
        for(int i=0; i<N; i++)
            scanf("%d %d %d", &tmp[i].a, &tmp[i].b, &tmp[i].c),
            tmp[i].num = 1, tmp[i].res = 0;
    
        sort(tmp, tmp+N, cmp);///根据a、b、c依次优先的优先级排序
                              ///使得在CDQ过程中能满足 ==> 左区间元素永比右区间小
    
        int len = 0;
        arr[len] = tmp[0];
        for(int i=1; i<N; i++){///去重
            if(tmp[i-1].a == tmp[i].a &&
               tmp[i-1].b == tmp[i].b &&
               tmp[i-1].c == tmp[i].c) arr[len].num++;
            else len++, arr[len] = tmp[i];
        }len++;
    
        CDQ(0, len);///开始CDQ分治
    
        for(int i=0; i<len; i++) ans[arr[i].res+arr[i].num-1] += arr[i].num;///计算每个三元组带来的贡献
        for(int i=0; i<N; i++) printf("%d
    ", ans[i]);///输出答案
        return 0;
    }
    View Code

    三维偏序拓展 ==> 二维平面上单点修改、矩阵求和 华科校赛H

    #include<bits/stdc++.h>
    #define LL long long
    #define lowbit(i) (i & (-i))
    using namespace std;
    const int maxn = 1e6 + 10;
    struct Query{
        int type;
        int x, y;
        int id, w, val;
        Query(){};
        Query(int t, int _x, int _y, int _id, int _w, int _val):
              type(t),x(_x),y(_y),id(_id),w(_w),val(_val){};
    
        bool operator < (const Query &rhs)const{
            if(this->x == rhs.x) return this->type < rhs.type;
            else return this->x <= rhs.x;
        };
    
    }tmp[maxn], query[maxn]; int qnum = 0;
    int c[maxn], ans[maxn]; int anum = 0;
    int N, W;
    
    inline void add(int i, int val)
    {
        while(i <= W){
            c[i] += val;
            i += lowbit(i);
        }
    }
    
    inline void Clear(int i)
    {
        while(i <= W){
            c[i] = 0;
            i += lowbit(i);
        }
    }
    
    int sum(int i)
    {
        int ret = 0;
        while(i > 0){
            ret += c[i];
            i -= lowbit(i);
        }return ret;
    }
    
    void CDQ(int L, int R)
    {
        if(R - L <= 1) return ;
        int M = L + ((R-L)>>1);
        CDQ(L, M);
        CDQ(M, R);
    
        int l = L, r = M, idx = 0;
        while(l < M && r < R){
            if(query[l] < query[r]){
                if(query[l].type == 1) add(query[l].y, query[l].val);///只统计左边区间的修改操作
                tmp[idx++] = query[l++];
            }else{
                if(query[r].type == 2) ans[query[r].id] += query[r].w * sum(query[r].y);///统计左边修改操作对右边的影响
                tmp[idx++] = query[r++];
            }
        }
    
        while(l < M) tmp[idx++] = query[l++];
        while(r < R){
            if(query[r].type == 2)
                ans[query[r].id] += query[r].w * sum(query[r].y);
            tmp[idx++] = query[r++];
        }
    
        for(int i=0; i<idx; i++){
            query[L+i] = tmp[i];
            if(tmp[i].type == 1)
                Clear(tmp[i].y);
        }
    }
    
    int main(void)
    {
        scanf("%d %d", &N, &W); W++;
        for(int i=0; i<N; i++){
            int type;
            scanf("%d", &type);
            if(type == 1){
                int x, y, H;
                scanf("%d %d %d", &x, &y, &H); x++, y++;
                query[qnum++] = Query(type, x, y, -2, -2, H);
            }else{
                int x1, y1, x2, y2;
                scanf("%d %d %d %d", &x1, &y1, &x2, &y2); x1++, y1++, x2++, y2++; 
                            ///将所有的 x、y 加一,方便操作(避免树状数组传 0 错误)
                query[qnum++] = Query(type, x2, y2, anum, 1, -2);///将矩阵求和查询变成四个前缀和查询
                query[qnum++] = Query(type, x1-1, y1-1, anum, 1, -2);
                query[qnum++] = Query(type, x2, y1-1, anum, -1, -2);
                query[qnum++] = Query(type, x1-1, y2, anum, -1, -2);
                anum++;
            }
        }
    
        CDQ(0, qnum);
    
        for(int i=0; i<anum; i++)
            printf("%d
    ", ans[i]);
    
        return 0;
    }
    View Code

    还有一些更加复杂的四维偏序、CDQ套CDQ 这些就以后再填坑吧....

  • 相关阅读:
    应急响应中find命令总结
    应急响应排查思路
    硬链接与软链接的区别
    Linux开机启动项总结
    android 開發常用網站
    epoll
    Qualcomm platform, the commonly used parameters of charger and battery in device tree file
    why not ovp protection ?
    Performance tuning
    Using adb over wifi
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/9098085.html
Copyright © 2011-2022 走看看