zoukankan      html  css  js  c++  java
  • CDQ分治笔记+例题

    CDQ分治是一种离线分治算法,它基于时间顺序对操作序列进行分治。

    看这样一个问题: 在一个三维坐标系中,有若干个点,每个点都有对应的坐标 ((X_i , Y_i , Z_i)) ,我们要对于每个点求所有满足 (X_j <=X_i, Y_j <= Y_i , Z_j <= Z_i) 的 j 的数量。

    考虑在二维平面上怎么做:我们可以将所有点按照 X 坐标排序,再用一个树状数组或者其他数据结构维护 Y 坐标,每次读到一个点就统计答案,然后将这个点加入树状数组。这样可以同时保证 X,Y 坐标都满足条件。

    如果这时再多一维,我们就没办法再用这种方法排序了,按照一维排序必然会打乱另外两维。这时我们考虑CDQ分治,先按 X 坐标排序,然后对序列进行分治,每次分出左右两段子序列,这时我们可以发现,左序列中每个点的 X 坐标小于右序列中点的 X 坐标,这时分别将左右序列按照 Y 坐标排序。统计答案时,只更新右序列中点的答案,只在树状数组中加入左序列的点。

    模版: P3810 【模板】三维偏序(陌上花开)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    const int N = 100005;
    const int M = 200005;
    int C[M], n, maxk, nn, ans[N];
    struct node {
    	int a, b, c, tot, num;
    } e[N];
    int lowbit(int x) {
    	return x & -x;
    }
    void add(int x, int k) {
    	for(; x <= maxk; x += lowbit(x)) C[x] += k;
    }
    int query(int x) {
    	int answ = 0; 
    	for(; x; x -= lowbit(x)) answ += C[x];
    	return answ;
    }
    bool cmp1(node x, node y) {
    	if(x.a == y.a && x.b == y.b) return x.c < y.c;
    	if(x.a == y.a) return x.b < y.b;
    	return x.a < y.a;
    }
    void cdq(int l, int r) {
    	int mid = (l + r) >> 1;
    	if(l != mid) cdq(l, mid);
    	if(mid + 1 != r) cdq(mid + 1, r);
    	node p[N]; 
    	int pt = 0, i = l, j = mid + 1;
    	while(i <= mid && j <= r) {//对于第二维归并排序
    		if(e[i].b <= e[j].b) add(e[i].c, e[i].tot), p[++pt] = e[i++];//插入
    		else e[j].num += query(e[j].c), p[++pt] = e[j++];//更新答案
    	}
    	while(i <= mid) add(e[i].c, e[i].tot), p[++pt] = e[i++];
    	while(j <= r) e[j].num += query(e[j].c), p[++pt] = e[j++];
    	for(int t = l; t <= mid; t++) add(e[t].c, -e[t].tot);//树状数组清空
    	for(int t = 1, u = l; t <= pt && u <= r; t++, u++) e[u] = p[t];//更新序列
    }
    int main() {
    //	freopen("data.in", "r", stdin);
    	scanf("%d%d", &nn, &maxk);
    	for(int i = 1, a, b, c; i <= nn; i++) {
    		scanf("%d%d%d", &a, &b, &c);
    		e[i].a = a, e[i].b = b, e[i].c = c;
    		e[i].num = 0; e[i].tot = 1;
    	}
    	sort(e + 1, e + 1 + nn, cmp1);//先按照第一维排序
    	n = 1;
    	for(int i = 2; i <= nn; i++) {
    		if(e[i].a == e[n].a && e[i].b == e[n].b && e[i].c == e[n].c) e[n].tot++;
    		else e[++n] = e[i];
    	}
    	cdq(1, n); 
    	for(int i = 1; i <= n; i++) {
    		ans[e[i].num + e[i].tot - 1] += e[i].tot;
    	}
    	for(int i = 0; i < nn; i++) printf("%d
    ", ans[i]);
    	return 0;
    }
    

    例题1 : P3157 [CQOI2011]动态逆序对

    考虑如和将其转化为三维偏序问题,我们要计算删除一个点对答案对贡献,即删除的这个点左边比它大的点的数量和右边比他小的点的数量,这样三维分别是删除顺序,序列中位置,元素大小。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    typedef long long lld;
    const int N = 100005;
    const int M = 50005;
    int n, m, pos[N], ansper[N], ansnxt[N];
    struct node {
       int x, y, type;
    } e[N], a[N];
    int C[N];
    int lowbit(int x) {
       return x & -x;
    }
    void add(int x, int k) {
       for(; x < N; x += lowbit(x)) C[x] += k;
    }
    int query(int x) {
       int summ = 0;
       for(; x; x -= lowbit(x)) summ += C[x];
       return summ;
    }
    void cdq_per(int l, int r) {
       if(l == r) return ;
       int mid = (l + r) >> 1;
       cdq_per(l, mid); cdq_per(mid + 1, r);
       node p[N];
       int i = l, j = mid + 1, pt = 0;
       while(i <= mid && j <= r) {
       	if(e[i].x <= e[j].x) add(e[i].y, 1), p[++pt] = e[i++];
       	else {
       		if(e[j].type == 0) p[++pt] = e[j++];
       		else ansper[e[j].type] += query(N - 1) - query(e[j].y), p[++pt] = e[j++];
       	}
       }
       while(i <= mid) add(e[i].y, 1), p[++pt] = e[i++];
       while(j <= r) {
       	if(e[j].type == 0) p[++pt] = e[j++];
       	else ansper[e[j].type] += query(N - 1) - query(e[j].y), p[++pt] = e[j++];
       }
       for(int k = l; k <= mid; k++) add(e[k].y, -1);
       for(int t = 1, u = l; t <= pt && u <= r; t++, u++) e[u] = p[t];
    }
    void cdq_nxt(int l, int r) {
       if(l == r) return ;
       int mid = (l + r) >> 1;
       cdq_nxt(l, mid); cdq_nxt(mid + 1, r);
       node p[N]; 
       int i = l, j = mid + 1, pt = 0;
       while(i <= mid && j <= r) {
       	if(a[i].x <= a[j].x) add(a[i].y, 1), p[++pt] = a[i++];
       	else {
       		if(a[j].type == 0) p[++pt] = a[j++];
       		else ansnxt[a[j].type] += query(a[j].y), p[++pt] = a[j++];
       	}
       }
       while(i <= mid) add(a[i].y, 1), p[++pt] = a[i++];
       while(j <= r) {
       	if(a[j].type == 0) p[++pt] = a[j++];
       	else ansnxt[a[j].type] += query(a[j].y), p[++pt] = a[j++];
       }
       for(int k = l; k <= mid; k++) add(a[k].y, -1);
       for(int t = 1, u = l; t <= pt && u <= r; t++, u++) a[u] = p[t];
    }
    int main() {
    //	freopen("data.in", "r", stdin);
       scanf("%d%d", &n, &m);
       for(int i = 1, x; i <= n; i++) {
       	scanf("%d", &x);
       	e[i].x = e[i].type = 0; e[i].y = x;
       	pos[x] = i;
       }
       for(int i = 1, x; i <= m; i++) {
       	scanf("%d", &x);
       	e[pos[x]].x = m - i + 1;
       	e[pos[x]].type = i;
       }
       for(int i = 1; i <= n; i++) a[i] = e[n - i + 1];
       cdq_per(1, n); 
       for(int i = 1; i <= n; i++) e[i] = a[n - i + 1];
       cdq_nxt(1, n);
       lld ans_sum = 0;
       for(int i = 1; i <= n; i++) {
       	ans_sum += i - query(e[i].y) - 1; 
       	add(e[i].y, 1);
       }
       int k = 1;
       while(k <= m) {
       	printf("%lld
    ", ans_sum); 
       	ans_sum -= (ansper[k] + ansnxt[k]);
       	k++;
       }
       return 0;
    }
    

    例题2 : P4169 [Violet]天使玩偶/SJY摆棋子

    。。。。。

  • 相关阅读:
    go语言切片
    sharding-jdbc分库分表配置,多数据源
    spring boot的配置文件
    go-micro生成项目
    自定义注解+aop实现jetcache功能扩展
    linux下mysql忘记密码解决方案
    MySQL 1130错误,无法远程连接
    Linux/UNIX 上安装 MySQL
    BarTender遇到的问题
    SourceTree安装使用
  • 原文地址:https://www.cnblogs.com/mcggvc/p/12343912.html
Copyright © 2011-2022 走看看