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; }
三维排序 ==> 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; }
三维偏序拓展 ==> 二维平面上单点修改、矩阵求和 华科校赛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; }
还有一些更加复杂的四维偏序、CDQ套CDQ 这些就以后再填坑吧....