您的位置:首页 > 其它

浅谈cdq分治

2018-10-01 22:23 211 查看
版权声明:蒟蒻写文章不容易啊,各位大佬转载要告诉我一声,QAQ https://blog.csdn.net/qq_38944163/article/details/82919189

这个东东是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

.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; }

[p]这就可以了,cdq分治还是蛮简单的嘛。

哈哈哈哈哈哈哈…… 

 

 

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: