浅谈CDQ分治与偏序问题
初识CDQ分治
CDQ分治是一个好东西,一直听着dalao们说所以就去学了下。
CDQ分治是我们处理各类问题的重要武器。它的优势在于可以顶替复杂的高级数据结构,而且常数比较小;缺点在于必须离线操作。 ——by __stdcall
其实CDQ分治名字听上去很高大上,其实和一般的分治没有特别大的区别,其大体流程如下:
- 将问题抽象为一个区间\([l,r]\)内的问题(废话)
- 分:将问题分解成左\([l,mid]\)右\([mid+1,r]\)两部分,然后递归操作
- 治:合并两个子问题,同时考虑到\([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分治与偏序问题的更多相关文章
- COGS 2479. [HZOI 2016]偏序 [CDQ分治套CDQ分治 四维偏序]
传送门 给定一个有n个元素的序列,元素编号为1~n,每个元素有三个属性a,b,c,求序列中满足i<j且ai<aj且bi<bj且ci<cj的数对(i,j)的个数. 对于100%的 ...
- BZOJ 3262: 陌上花开 [CDQ分治 三维偏序]
Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当 ...
- 洛谷P3810 陌上花开 CDQ分治(三维偏序)
好,这是一道三维偏序的模板题 当然没那么简单..... 首先谴责洛谷一下:可怜的陌上花开的题面被无情的消灭了: 这么好听的名字#(滑稽) 那么我们看了题面后就发现:这就是一个三维偏序.只不过ans不加 ...
- 深谈CDQ分治
关于CDQ分治我想我自己做过前面的题应该会了这种思想了吧,然后我是真的“会了”. 我想针对于偏序问题我是会了,我现在只会三维偏序了,脑子都是疼的. 但是 CDQ分治最主要的还是基于时间方面的分治思想, ...
- 【算法】CDQ分治 -- 三维偏序 & 动态逆序对
初次接触CDQ分治,感觉真的挺厉害的.整体思路即分而治之,再用之前处理出来的答案统计之后的答案. 大概流程是(对于区间 l ~ r): 1.处理 l ~mid, mid + 1 ~ r 的答案: 2. ...
- NEUOJ 1702:撩妹全靠魅力值(CDQ分治三维偏序)
http://acm.neu.edu.cn/hustoj/problem.php?id=1702 思路:三维偏序模板题,用CDQ分治+树状数组或者树套树.对于三元组(x,y,z),先对x进行排序,然后 ...
- cdq分治·三维偏序问题
转载自FlashHu大佬的博客CDQ分治总结(CDQ,树状数组,归并排序),在讲述部分有部分删改,用了自己的代码 CDQ分治的思想 CDQ分治是基于时间的离线分治算法.这一类分治有一个重要的思想——用 ...
- CDQ分治 三维偏序
这应该是一道CDQ分治的入门题目 我们知道,二维度的偏序问题直接通过,树状数组就可以实现了,但是三维如何实现呢? 我记得以前了解过一个小故事,应该就是分治的. 一个皇帝,想给部下分配任务,但是部下太多 ...
- BZOJ 2244: [SDOI2011]拦截导弹 (CDQ分治 三维偏序 DP)
题意 略- 分析 就是求最长不上升子序列,坐标取一下反就是求最长不下降子序列,比较大小是二维(h,v)(h,v)(h,v)的比较.我们不看概率,先看第一问怎么求最长不降子序列.设f[i]f[i]f[i ...
随机推荐
- MVC与单元测试实践之健身网站(五)-系统信息、前台入口
Fit项目停滞了一段时间,现在继续吧.上一篇完成了动作文本和配图的添加.编辑等内容.接下来要完成的是后台的最后一个模块:系统信息:以及前台的入口:关于注册.登录.修改密码等. 一 系统信息 a) 用户 ...
- Kotlin入门(19)Android的基础布局
线性布局线性布局LinearLayout是最常用的布局,顾名思义,它下面的子视图像是用一根线串了起来,所以其内部视图的排列是有顺序的,要么从上到下垂直排列,要么从左到右水平排列.排列顺序只能指定一维方 ...
- Git服务器配置及本地克隆提交、服务器获取
1.服务器Git安装配置 相关链接 相关链接 注意ssh-keygen .修改权限 权限: 相关链接 2.本地获取 git clone name@ip:服务器项目位置 相关链接 3.创建 ...
- 你不可不知的Java引用类型之——软引用
定义 软引用是使用SoftReference创建的引用,强度弱于强引用,被其引用的对象在内存不足的时候会被回收,不会产生内存溢出. 说明 软引用,顾名思义就是比较"软"一点的引用. ...
- (后端)异常不仅仅是try/catch
前言 编程时我们往往拿到的是业务流程正确的业务说明文档或规范,但实际开发中却布满荆棘和例外情况,而这些例外中包含业务用例的例外,也包含技术上的例外.对于业务用例的例外我们别无它法,必须要求实施人员与 ...
- 【HANA系列】SAP HANA XS的JavaScript API详解
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA XS的Java ...
- 通信原理之OSI七层参考模型(一)
1.什么是计算机网络 谈计算机通信原理当然离不开计算机网络,那么什么是计算机网络.官方定义:计算机网络是由两台或两台以上的计算机通过网络设备连接起来所组成的一个系统,在这个系统中计算机与计算机之间可以 ...
- 第 16 章 C 预处理器和 C 库(预定义宏)
/*------------------------------------- predef.c -- 预定义宏和预定义标识符 ------------------------------------ ...
- VRS的GPS/BDS双系统网元固定存在的问题
问题如下:部分网元固定卫星数少于2个. 另外:北方xinkong的网元组网也存在问题
- 17秋 软件工程 团队第五次作业 Alpha Scrum9
17秋 软件工程 团队第五次作业 Alpha Scrum9 今日完成的任务 世强:APP后端部门申请状态: 港晨:主页面代码实现: 树民:完善超级管理员web后端: 伟航:设置页面和侧边栏的原型: 陈 ...