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\n", 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\n", 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\n", ans[i]);

    return 0;
}
View Code

 

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

posted @ 2018-05-27 23:26  qwerity  阅读(211)  评论(0编辑  收藏  举报