初识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

浅谈CDQ分治与偏序问题的更多相关文章

  1. COGS 2479. [HZOI 2016]偏序 [CDQ分治套CDQ分治 四维偏序]

    传送门 给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足i<j且ai<aj且bi<bj且ci<cj的数对(i,j)的个数. 对于100%的 ...

  2. BZOJ 3262: 陌上花开 [CDQ分治 三维偏序]

    Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当 ...

  3. 洛谷P3810 陌上花开 CDQ分治(三维偏序)

    好,这是一道三维偏序的模板题 当然没那么简单..... 首先谴责洛谷一下:可怜的陌上花开的题面被无情的消灭了: 这么好听的名字#(滑稽) 那么我们看了题面后就发现:这就是一个三维偏序.只不过ans不加 ...

  4. 深谈CDQ分治

    关于CDQ分治我想我自己做过前面的题应该会了这种思想了吧,然后我是真的“会了”. 我想针对于偏序问题我是会了,我现在只会三维偏序了,脑子都是疼的. 但是 CDQ分治最主要的还是基于时间方面的分治思想, ...

  5. 【算法】CDQ分治 -- 三维偏序 & 动态逆序对

    初次接触CDQ分治,感觉真的挺厉害的.整体思路即分而治之,再用之前处理出来的答案统计之后的答案. 大概流程是(对于区间 l ~ r): 1.处理 l ~mid, mid + 1 ~ r 的答案: 2. ...

  6. NEUOJ 1702:撩妹全靠魅力值(CDQ分治三维偏序)

    http://acm.neu.edu.cn/hustoj/problem.php?id=1702 思路:三维偏序模板题,用CDQ分治+树状数组或者树套树.对于三元组(x,y,z),先对x进行排序,然后 ...

  7. cdq分治·三维偏序问题

    转载自FlashHu大佬的博客CDQ分治总结(CDQ,树状数组,归并排序),在讲述部分有部分删改,用了自己的代码 CDQ分治的思想 CDQ分治是基于时间的离线分治算法.这一类分治有一个重要的思想——用 ...

  8. CDQ分治 三维偏序

    这应该是一道CDQ分治的入门题目 我们知道,二维度的偏序问题直接通过,树状数组就可以实现了,但是三维如何实现呢? 我记得以前了解过一个小故事,应该就是分治的. 一个皇帝,想给部下分配任务,但是部下太多 ...

  9. BZOJ 2244: [SDOI2011]拦截导弹 (CDQ分治 三维偏序 DP)

    题意 略- 分析 就是求最长不上升子序列,坐标取一下反就是求最长不下降子序列,比较大小是二维(h,v)(h,v)(h,v)的比较.我们不看概率,先看第一问怎么求最长不降子序列.设f[i]f[i]f[i ...

随机推荐

  1. Mac 的mysql5.7没有配置文件,如何解决only_full_group_by 问题

    数据库版本是5.7.19,在写语句的时候,只要涉及ORDER BY,就会报错, ERROR 1055 (42000): Expression #7 of SELECT list is not in G ...

  2. vs2012碰到生成时报该错误:项目中不存在目标“GatherAllFilesToPublish”

    手头一个vs2010升级到vs2012后,web项目发布到本地目录时项目报错:“该项目中不存在目标“GatherAllFilesToPublish”” 通过谷歌大神的帮助,找到了解决方法.共享之. 原 ...

  3. [20171107]dbms_shared_pool.pin补充.txt

    [20171107]dbms_shared_pool.pin补充.txt --//上午的测试,做一些补充,主要还是一些理解问题. 1.环境:SCOTT@book> @ &r/ver1 P ...

  4. 洗礼灵魂,修炼python(11)--python函数,模块

    前面的章节你如果看懂了,基本算是入门了七八了,不过如果你以为python就这么点东西,你觉得很简单啊,那你就错了,真正的东西在后面,前面我说的几大核心其实也不是多么高深多么厉害的,那些东西是基础很常用 ...

  5. 使用Tampermonkey(油猴) 插件,重新实现了,百度搜索热点过滤功能

    昨天晚上,花了点时间学习了Chrome插件的制作方法,并书写了<Chrome 百度搜索热点过滤插件 - 开源软件>这一文章,简单地介绍自己实现的百度搜索热点过滤神器的原理和使用方式,并进行 ...

  6. 探索哪个进程使磁盘I/O升高

    如果生产环境中磁盘使用率突然升高,却不知道因为哪个应用程序导致的,这个时候我们可以使用pidstat命令来查看,比如 Linux .el7.x86_64 (ip.ec2.internal) _x86_ ...

  7. 4.5Python数据类型(5)之列表类型

    返回总目录 目录: 1.列表的定义 2.列表的常规操作 3.列表的额外操作 (一)列表的定义: 列表的定义 [var1, var2, --, var n ] # (1)列表的定义 [var1, var ...

  8. January 06th, 2018 Week 01st Saturday

    In life the most interesting things tend to happen when you are on your way to do something else. 生活 ...

  9. input 属性radio中设置checked 不生效

    同一个页面中有许多地方都用到了单选按钮并设置了默认选中 , 结果在运行的时候发现单选按钮没有被默认选中 由于是复制然后修改个别属性 ,然后直接使用的 , 所以name值忘记修改了 , 单选框是根据na ...

  10. SAP CRM 开发学习资料和教程整理【不定时更新】

    本文链接:http://www.cnblogs.com/hhelibeb/p/6276929.html 首先是SAP网站上面的相关内容 SAP Customer Relationship Manage ...