浅谈cdq分治
这个东东是HN著名女选手陈丹琦发明的。
嗯……
其实还是挺简单的吧,就是把一个区间分成两部分,然后看前半部分对后半部分的贡献,然后一路分治下去就好了。
首先扔一个简单的题目:https://www.luogu.org/problemnew/show/P1908
就是求逆序对,刚开始用树状数组在线写WA QAQ
然后发现是边界问题 QAQ
愉快地改完后用cdq写了一遍,1A!!!太感动了。
果然还是树状数组跑得快一点。。
那么就简单讲讲怎么做吧。
我们先每把当前序列分成两份,这就保证了左边部分的坐标是小于右边的嘛(显而易见),当我们分治下去的时候可以 随便做归并排序,那么对于两个有序序列,我们可以用two pointers来乱搞,那么就可以搞出来了呀。
看代码吧:
[code]#include<bits/stdc++.h> #define N 500005 using namespace std; int sz, n, a , b , A , B , C , ans ; long long s; void cdq(int l, int r){ if(l == r) return; int mid = (l + r) >> 1; cdq(l, mid); cdq(mid + 1, r);//分治下去 int sz1 = 0, sz2 = 0, sz3 = 0, p = 0; for(int i = l; i <= mid; i ++) A[++ sz1] = b[i]; //先拿出来,方便处理,A为左边的部分,B为右边的部分 for(int i = mid + 1; i <= r; i ++) B[++ sz2] = b[i]; for(int i = 1; i <= sz2; i ++){ for(;A[p + 1] <= B[i] && p < sz1;) C[++ sz3] = A[++ p];//C是临时用来储存A、B合并的有序数组 ans[i + mid] += sz1 - p;//即左边对当前为i的贡献 C[++ sz3] = B[i]; } for(;p < sz1; ) C[++ sz3] = A[++ p];//边界处理一下 for(int i = l; i <= r; i ++) b[i] = C[i - l + 1];//把有序数组反赋回去 } int main(){ scanf("%d", &n); for(int i = 1; i <= n; i ++) scanf("%d", &b[i]), a[i] = b[i]; sort(b + 1, b + 1 + n); int sz = unique(b + 1, b + 1 + n) - b; for(int i = 1; i <= n; i ++) a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b;//离散化 for(int i = 1; i <= n; i ++) b[i] = a[i]; cdq(1, n); for(int i = 1; i <= n; i ++) s += ans[i];//累计答案 printf("%lld", s); return 0; }//愉快的结束
不过瘾,再来一题:https://www.luogu.org/problemnew/show/P3810
这就是经典的三维偏序
同上题,分治减少一维,再用树状数组做剩下两维。
代码:
[code]#include<bits/stdc++.h> #define N 400005 #define lowbit(x) x & -x using namespace std; struct cdq_is_good{ int a, b, c, id, idd; }a , A ; bool cmp(cdq_is_good x, cdq_is_good y){ if(x.c != y.c)return x.c < y.c; if(x.b != y.b)return x.b < y.b; return x.a < y.a; } int tree ; void update(int x, int y){ for(;x < N;x += lowbit(x)) tree[x] += y; } int query(int x){ int ret = 0; for(;x;x -= lowbit(x)) ret += tree[x]; return ret; } int del , ans , f , n, k; void cdq(int l, int r){ if(l == r) return; int mid = (l + r) >> 1, p = l - 1, sz = 0, delsz = 0; cdq(l, mid); cdq(mid + 1, r); for(int i = mid + 1; i <= r; i ++){ for(;a[p + 1].b <= a[i].b && p < mid;){A[++ sz] = a[++ p]; update(a[p]这就可以了,cdq分治还是蛮简单的嘛。.a, 1); del[++ delsz] = a[p].a;}//树状数组处理剩下两维 ans[a[i].id] += query(a[i].a); A[++ sz] = a[i]; } for(;p < mid;) A[++ sz] = a[++ p]; for(int i = l; i <= r; i ++) a[i] = A[i - l + 1]; for(int i = 1; i <= delsz; i ++) update(del[i], - 1);//这样可以避免不必要的时间开销,就像树分治一样,千万不能用memset } int main(){ scanf("%d%d", &n, &k); for(int i = 1; i <= n; i ++) scanf("%d%d%d", &a[i].a, &a[i].b, &a[i].c), a[i].id = a[i].idd = i; sort(a + 1, a + 1 + n, cmp);//先排序去掉一维 int l = 1; for(int i = 1; i <= n + 1; i ++){ if(!(a[i].a == a[l].a && a[i].b == a[l].b && a[i].c == a[l].c)){//处理相同的情况 for(;l < i;) a[l ++].idd = a[i - 1].id; } } cdq(1, n); for(int i = 1; i <= n; i ++) f[ans[a[i].idd]] ++; for(int i = 0; i < n; i ++) printf("%d\n", f[i]); return 0; }
哈哈哈哈哈哈哈……
阅读更多
- BZOJ 3237 浅谈CDQ分治+带撤销并查集
- 主席树+CDQ分治+整体二分
- BZOJ 2716 Violet 3 天使玩偶 CDQ分治
- 3295: [Cqoi2011]动态逆序对 CDQ分治
- BZOJ 3262 陌上花开 (CDQ分治)
- bzoj 3295 [Cqoi2011]动态逆序对(cdq分治,BIT)
- BZOJ 1176 && BZOJ 2683 CDQ分治
- bzoj4237 稻草人 CDQ分治
- 【BZOJ1176】Mokia(CDQ分治)
- CDQ分治 【bzoj2683 && bzoj1176】
- HDU 5324 Boring Class(CDQ分治)
- 【BZOJ 3262】陌上开花 CDQ分治
- [CDQ分治] BZOJ 2244 [SDOI2011]拦截导弹
- 【bzoj3295】动态逆序对 CDQ分治
- BZOJ 3262 陌上花开 树套树 (CDQ分治)
- Codeforces848C Goodbye Souvenir -- CDQ分治
- [Uva 11990] "Dynamic" Inversion (CDQ分治入门)
- cdq分治
- [BOI2007]摩基亚Mokia (cdq分治)
- 初学CDQ分治-NEU1702