初识CDQ分治

CDQ分治是一个好东西,一直听着dalao们说所以就去学了下。

CDQ分治是我们处理各类问题的重要武器。它的优势在于可以顶替复杂的高级数据结构,而且常数比较小;缺点在于必须离线操作。 ——by __stdcall

其实CDQ分治名字听上去很高大上,其实和一般的分治没有特别大的区别,其大体流程如下:

  1. 将问题抽象为一个区间\([l,r]\)内的问题(废话)
  2. 分:将问题分解成左\([l,mid]\)右\([mid+1,r]\)两部分,然后递归操作
  3. 治:合并两个子问题,同时考虑到\([l,mid]\)内的修改对\([mid+1,r]\)内的查询产生的影响。即,用左边的子问题帮助解决右边的子问题。

这里特别注意CDQ分治与一般分治的区别:普通分治在合并两个子问题的过程中,左右区间内的问题不会互相影响。


经典应用——三维偏序

我们从一道模板题来看看CDQ的具体实现:Luogu P3810

首先考虑经典的二维偏序:逆序对

这个鬼东西不是就一个归并排序or权值树状数组的事情么

我们想一下归并排序的原理,在归并的过程中(数组已经有序),那么我左边的并且坐标大于右边的坐标个数其实就是逆序对个数。

因此这也算是个简单的CDQ吧

现在我们考虑三维偏序,我们考虑先对数组总体排个序,这样在操作的过程中总有\(a_i\le a_j(i<j)\)(即使我们将区间一分为二那么右边的数的\(a_i\)始终大于左边。

然后对于第二维\(y_i\),我们考虑一下处理方法。

假设现在处理区间\([l,r]\),而此前我们已经通过递归处理好了\([l,mid]\)和\([mid+1,r]\)的答案。

那我们把\([l,mid]\)和\([mid+1,r]\)分别按\(y_i\)排个序,这样第二维也有了上面的性质。

再考虑怎么计算左边和右边的偏序关系,我们可以维护两个指针\(i,j\),每次我们将\(j\)后移一位以表示再加入一个数,此时若\(y_i\le y_j\)则不断后移\(i\),并且将\(z_i\)加入权值树状数组。

然后现在对于右边的每一个数:

  • 在权值树状数组上所有的数的\(x_i\)都小于它(因为排了序)
  • 在权值树状数组上所有的数的\(y_i\)都小于它(因为上面的指针偏移统计)

那么只要找\(z_i\)小于它的数个数即可,这个我们直接在树状数组上找即可。

复杂度是比较迷的\(O(n\log n)\),不过由于CQD的常数很小所以可以轻松跑过缅怀各位写树套树的dalao

下面上CODE

#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
const int N=100005;
struct data
{int x,y,z,num,sum;bool operator ==(const data &s) const { return x==s.x&&y==s.y&&z==s.z; }
}a[N],q[N];
int n,cnt,m,bit[N<<1],ans[N],tot;
inline char tc(void)
{static char fl[100000],*A=fl,*B=fl;return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag;
}
inline void write(int x)
{if (x>9) write(x/10);putchar(x%10+'0');
}
inline bool cmpx(data a,data b)
{if (a.x==b.x&&a.y==b.y) return a.z<b.z;if (a.x==b.x) return a.y<b.y; return a.x<b.x;
}
inline bool cmpy(data a,data b)
{if (a.y==b.y) return a.z<b.z; return a.y<b.y;
}
inline int lowbit(int x)
{return x&-x;
}
inline void add(int x,int y)
{for (;x<=m;x+=lowbit(x)) bit[x]+=y;
}
inline int get(int x)
{int res=0; for (;x;x-=lowbit(x)) res+=bit[x]; return res;
}
inline void CDQ(int l,int r)
{if (l==r) return; int mid=l+r>>1,id=l;CDQ(l,mid); CDQ(mid+1,r); sort(q+l,q+mid+1,cmpy); sort(q+mid+1,q+r+1,cmpy);for (register int i=mid+1;i<=r;++i){while (id<=mid&&q[id].y<=q[i].y) add(q[id].z,q[id].num),++id; q[i].sum+=get(q[i].z);}for (register int i=l;i<id;++i) add(q[i].z,-q[i].num);
}
int main()
{//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);register int i; read(n); read(m);for (i=1;i<=n;++i) read(a[i].x),read(a[i].y),read(a[i].z);for (sort(a+1,a+n+1,cmpx),a[n+1]=(data){-1,-1,-1},i=cnt=1;i<=n;++i)if (a[i]==a[i+1]) ++cnt; else q[++tot]=a[i],q[tot].num=cnt,cnt=1;for (CDQ(1,tot),i=1;i<=tot;++i) ans[q[i].sum+q[i].num-1]+=q[i].num;for (i=0;i<n;++i) write(ans[i]),putchar('\n'); return 0;
}

关于更复杂的问题

其实我也不会,不过对于一般的高维偏序,我们可以CDQ套CDQ,反正一般k维偏序用CDQ的复杂度就是\(O(n\log^{k-1} n)\)

因此维数太大时还是使用K-d tree

转载于:https://www.cnblogs.com/cjjsb/p/9538595.html

浅谈CDQ分治与偏序问题相关推荐

  1. 浅谈根号分治——暴力的美学

    根号分治 根号分治的概念 [模板]P3396 哈希冲突 CF103D Time to Raid Cowavans 根号分治的概念 根号分治是一种优化暴力算法. 我个人的理解就是这东西跟分块差不多.但应 ...

  2. 分治、CDQ分治小结

    分治.CDQ分治小结 A Summary for Divide and Conquer 0. Anouncement 本文部分图片以及部分内容来自互联网,内容过多就不一一注明出处了,冒犯之处还请海涵. ...

  3. CDQ分治 + 树状数组 ---- C. Goodbye Souvenir(三维偏序+思维)

    题目链接 题目大意: 给定长度为nnn的数组, 定义数字XXX在[l,r][l,r][l,r]内的值为数字XXX在[l,r][l,r][l,r]内最后一次出现位置的下标减去第一次出现位置的下标 给定m ...

  4. CDQ分治嵌套模板:多维偏序问题

    CDQ分治2 CDQ套CDQ:四维偏序问题 题目来源:COGS 2479 偏序 #define LEFT 0 #define RIGHT 1struct Node{int a,b,c,d,bg;}; ...

  5. HDU - 5517 Triple(三维偏序-二维树状数组/CDQ分治)

    题目链接:点击查看 题目大意:给出 n 个二元对 ( a , b ) 和 m 个三元对 ( c , d , e ),对于所有 b == e 的二元对和三元对,可以通过某种运算形成一个新的三元对 ( a ...

  6. 洛谷 - P3810 【模板】三维偏序(陌上花开)(CDQ分治套树状数组)

    题目链接:点击查看 题目大意:给出 n 个点,每个点有三个属性 a , b , c ,对于每个点 i 来说,求出有多少个 j 满足 a[ j ] <= a[ i ] && b[ ...

  7. 【牛客NOIP模拟】 牛牛的RPG游戏【二维偏序】【任意坐标斜率优化】【CDQ 分治】【李超线段树】

    题意: n×mn\times mn×m 的网格图,每个点有两个权值 vali,j,bufi,jval_{i,j},buf_{i,j}vali,j​,bufi,j​,从 (1,1)(1,1)(1,1) ...

  8. 【原创】从BZOJ2683 简单题中 整 CDQ分治解决三维偏序

    CDQ分治 题目描述 你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作: 命令 参数限制 内容 1 x y A 1<=x,y<=N,A是正整数 将格子 ...

  9. 三维偏序/cdq分治/

    三维偏序---cdq分治 cdq分治概述 二维偏序概述 二维偏序例题分析 三维偏序概述 例题分析 cdq分治概述 前置知识:(如果不懂要先去了解分治) > 分治: > 分而治之,将原问题不 ...

最新文章

  1. 简单读懂微生物基因组的泛基因组学
  2. 十周第一次课(5月25日)
  3. 【TCP/IP详解 卷一:协议】第十二章 广播和多播
  4. 系统集成项目管理之项目采购管理
  5. 使用IntelliJ IDEA开发SpringMVC网站(一)开发环境
  6. 如何自学python到做项目-总算明白如何通过项目学习python
  7. 【Groovy】集合遍历 ( 使用集合的 findAll 方法查找集合中符合匹配条件的所有元素 | 代码示例 )
  8. [云炬创业基础笔记]第二章创业者测试8
  9. 团子大家族(clannad)
  10. HALCON示例程序train_characters_ocr.hdev使用SVM分类器训练字体
  11. 一起来看流星雨剧情简介/剧情介绍/剧情分集介绍第三十集
  12. 使用nssm管理tomcat服务操作步骤
  13. 汇川AM系列Modbus通信设置
  14. Android根据经纬度计算距离
  15. 【数据库】数据字典表
  16. SVN常用的9大图标
  17. xbox360链接pc_如何在Windows PC上使用Xbox One控制器
  18. java.sql.SQLSyntaxErrorException: Table 'ph.tbl_user' doesn't exist
  19. pb删除指定文件夹下所有文件
  20. NXP i.MX8M Plus赋能边缘机器学习,启扬IAC-IMX8MP-Kit开发板

热门文章

  1. Martin Fowler 经典软件著作合集
  2. 20140505 科技脉搏 - “社交”这棵老树,依然在开着新花
  3. (12) 需求征集 -- 序列管理、编号管理
  4. 类库如何读取配置文件(app.config)?
  5. 利用哈夫曼树编码与译码
  6. Python中列表的增、删、改、查、排序
  7. python类型提示模块包_Python checktypes包_程序模块 - PyPI - Python中文网
  8. 微信一键设置“姓氏头像”,学起来!
  9. 测试用例集-8.公交卡测试用例
  10. 翁恺老师C语言学习笔记(七)函数