当前位置: 首页 > news >正文

总结-CDQ 分治

关于 CDQ 分治

CDQ 分治是一种思想而不是具体的算法,并且必须离线处理,用于维护具有偏序限制的问题。

偏序可以理解为大小关系。

经典三维偏序

CDQ 分治的经典应用。

给定每个元素,每个元素都有三个属性 \((x,y,z)\),要求统计所有满足三个偏序条件时的价值。

标准方法:sort 排序维护第一维偏序,用归并维护第二维偏序,用数据结构(树状数组、线段树)维护第三维偏序。

原理是在处理第二维的时候,使每一个左半区间元素对于每一个右半区间元素一定满足第一维的偏序,同时维护并统计第三维。

给个标准的框架:

归并版本:

归并排序版本的优点是常数小,缺点是必须要后序遍历并且偏序条件所关联的维度相同,要求统计价值时没有严格的先后顺序

经典的有严格的先后顺序的题目如 CDQ 分治套 DP 等,由于转移时要求所有先导状态都已被处理,所以这种题不能用归并版本来写,而需要用中序遍历的 sort 排序版本。

例题:[CQOI2011] 动态逆序对 - 洛谷

#define lowbit(x) ( x&(-x) )
int T[N];inline void update(int x)
{ for(;x<=n;x+=lowbit(x)) /*维护价值*/; }inline void Clear(int x)
{ for(;x<=n;x+=lowbit(x)) T[x]=0; }inline int query(int x)
{int sum=0;for(;x>0;x-=lowbit(x))/*统计价值*/return sum;
}struct ask
{ int x,y,z; }Q[N],tmp[N];
int ans[N];bool rule_y(ask u,ask v)
{ return u.y<v.y; }bool rule_x(ask u,ask v)
{ return u.x<v.x; }void cdq(int l,int r)
{if(l==r) return ;int mid=l+r>>1;cdq(l,mid),cdq(mid+1,r);// 后序遍历int i=l,j=mid+1,pos=l;for(;j<=r;j++){while(i<=mid && Q[i].y<Q[j].y){update(Q[i].z);// 树状数组维护第三维tmp[pos++]=Q[i++];}/**/// 这里要统计答案tmp[pos++]=Q[j];}for(int k=l;k<i;k++) Clear(Q[k].z);//清空树状数组while(i<=mid) tmp[pos++]=Q[i++];for(int k=l;k<=r;k++)Q[k]=tmp[k];/**/// 若有需要,这里可能也需要统计答案并清空
}signed main()
{/**/// 输入和预处理sort(Q+1,Q+n+1,rule_x);cdq(1,n);// 计算最终答案并输出return 0;
}

给出一个不能用归并版本的题目:序列 - 洛谷

sort 版本

后序遍历版本

就是将原来归并排序的过程用 sort 来硬排序,常数大,一般都不这么写。

例题:陌上花开 - 洛谷

#define mid (l+r>>1)
void CDQ(int l,int r)
{if(l==r) return ;//递归左右 并 按第二维排序CDQ(l,mid);     sort(p+l,p+mid+1,rule_y);CDQ(mid+1,r);   sort(p+mid+1,p+r+1,rule_y);// 按第二维排序后,第一维的顺序虽被打乱// 但由于我们是按 第一维 从小到大的顺序 二分// 所以对于 左半边的任意x 仍然小于 右半边的任意x// 我们计算左半边对右半边贡献的价值时仍满足 第一维的偏序条件//归并左右 双指针计算价值int i=l,j=mid+1;for(;j<=r;j++){while(i<=mid && p[i].y<=p[j].y)//左半边的i 可以 为右半边的 j 在第二维提供价值{update(p[i].z,p[i].cnt);//将i的第三维计入树状数组i++;}p[j].ans+=query(p[j].z);//计算第三维的价值}//清空树状数组for(int k=l;k<i;k++)update(p[k].z,-p[k].cnt);
}

中序遍历版本

一个用于处理有严格统计顺序并且偏序条件关联不同维度的题目,比如典型的 CDQ 分治套 DP 题目 序列 - 洛谷。

struct node
{int p,l,r,x;
}Q[N];#define lowbit(x) ( x&(-x) )
int T[N];inline void update(int x,int val)
{for(;x<=M;x+=lowbit(x))ckmax(T[x],val);
}inline void Clear(int x)
{for(;x<=M;x+=lowbit(x))T[x]=0;
}inline int query(int x)
{int ans=0;for(;x>0;x-=lowbit(x))ckmax(ans,T[x]);return ans;
}bool rule_x(node u,node v)
{ return u.x<v.x; }bool rule_l(node u,node v)
{ return u.l<v.l; }bool rule_p(node u,node v)
{ return u.p<v.p; }void CDQ(int l,int r)
{if(l==r) return ;int mid=l+r>>1;CDQ(l,mid);sort(Q+l,Q+mid+1,rule_x);sort(Q+mid+1,Q+r+1,rule_l);int i=l,j=mid+1;while(j<=r){while(i<=mid && Q[i].x<=Q[j].l){update(Q[i].r,dp[Q[i].p]);i++;}ckmax(dp[Q[j].p],query(Q[j].x)+1);j++;}for(int k=l;k<i;k++) Clear(Q[k].r);sort(Q+l,Q+r+1,rule_p);CDQ(mid+1,r);
}

四维偏序

大体上就是将用一次排序维护第一维,归并处理第二和第三维,用数据结构处理第四维。

http://www.wxhsa.cn/company.asp?id=5850

相关文章:

  • 【初赛】计算机语言 - Slayer
  • 深入浅出RocketMQ客户端编程
  • Win10玩LOL弹窗
  • 洞察中国HR SaaS薪酬市场:2025企业数字化转型中的选型策略
  • 9.16 一些记录
  • Week 1 Homework
  • 溢出存储变量
  • retrieving repo key for OS unencrypted from
  • 3. Explain详解与索引最佳实践
  • 软工个人项目作业
  • 异地办公文件同步,多台设备如何无缝同步最新教程
  • CSP-S模拟22
  • 详细介绍:【系统分析师】2025年上半年真题:论文及解题思路
  • 表格如何设置多人在线编辑?坚果云实时编辑,告别版本冲突!
  • 白嫖党狂喜!爆肝一下午搞定 URL 转 HTML 幻灯片神器,ISlide 9900 资源点从此是路人
  • Codeforces 2144E2 Looking at Towers (difficult version) 题解 [ 蓝 ] [ 线性 DP ] [ 树状数组 ]
  • 实战有效的Web时序攻击技术剖析
  • 22222222 - idle
  • 继承
  • 我们究竟在用钱交换什么?
  • jupyterLab如何使用
  • HyperWorks许可监控
  • C++拷贝构造函数详解:从浅拷贝到深拷贝
  • ThreadLocal
  • K8S探针
  • 模拟赛
  • bug1
  • C#第十二天 025
  • 选择语句的机器级表示
  • pip常用命令