经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
CDQ分治和三维偏序
来源:cnblogs  作者:逆行伐仙  时间:2023/10/18 8:53:42  对本文有异议

专题:CDQ 分治

本页面将完整介绍 CDQ 分治。

简介

CDQ 分治是一种思想而不是具体的算法,与动态规划类似。目前这个思想的拓展十分广泛,依原理与写法的不同,大致分为三类:

  • 解决和点对有关的问题。
  • 1D 动态规划的优化与转移。
  • 通过 CDQ 分治,将一些动态问题转化为静态问题。

CDQ 分治的思想最早由 IOI2008 金牌得主陈丹琦在高中时整理并总结,它也因此得名。

解决和点对有关的问题

这类问题多数类似于「给定一个长度为 nn 的序列,统计有一些特性的点对 (i,j)(i,j) 的数量/找到一对点 (i,j)(i,j) 使得一些函数的值最大」。

CDQ 分治解决这类问题的算法流程如下:

  1. 找到这个序列的中点 ;
  2. 将所有点对 (i,j)(i,j) 划分为 33 类:
    • 1imid1jmid 的点对;
    • 1imidmid+1jn 的点对;
    • mid+1inmid+1jn 的点对。
  3. 将 (1,n)(1,n) 这个序列拆成两个序列 (1,mid)(1,mid) 和 (mid+1,n)(mid+1,n) 。此时第一类点对和第三类点对都在这两个序列之中;
  4. 递归地处理这两类点对;
  5. 设法处理第二类点对。

可以看到 CDQ 分治的思想就是不断地把点对通过递归的方式分给左右两个区间。

在实际应用时,我们通常使用一个函数 solve(l,r) 处理 lirljr 的点对。上述算法流 程中的递归部分便是通过 solve(l,mid) 与 solve(mid,r) 来实现的。剩下的第二类点对则需要额外设计算法解决。

典型例题1:LOJ112/洛谷P3810 三维偏序(陌上开花)

分析:

三维偏序(陌上开花)是 CDQ 分治的经典问题。

假设我们现在写好了 solve (l, r) ,并且通过递归搞定了 solve (l, mid) 和 solve(mid+1,r) 。现在我们要做的,就是统计满足 limidmid+1jr 的点对 (i, j)(i,j) 中,有多个点对还满足 i<jai?<aj?bi?<bj? 的限制条件。

稍微思考一下就会发现,那个 i<j 的限制条件没啥用了:既然 i 比 mid 小, 比 mid 大,那 i 肯定比 j 要小。 现在还剩下两个限制条件: ai?<aj? 与 bi?<bj? , 根据这个限制条件我们就可以枚举 , 求出有多少个满足条件的 i。

为了方便枚举,我们把 (l,mid) 和 (mid+1,r) 中的点全部按照 a 的值从小到大排个序。之后我们依次枚举每一 个 j , 把所有 ai?<aj? 的点 i 全部揷入到某种数据结构里(这里我们选择树状数组)。此时只要查询树状数组里有多少个点的 b 值是小于 bj? 的,我们就求出了对于这个点 j ,有多少个 ii 可以合法匹配它了。

当我们揷入一个 b 值等于 xx 的点时,我们就令树状数组的 xx 这个位置单点 +1+1,而查询树状数组里有多少个点小于 xx 的操作实际上就是在求前缀和,只要我们事先对于所有的 bb 值做了离散化,我们的复杂度就是对的。

对于每一个 j,我们都需要将所有 ai?<aj? 的点 i 揷入树状数组中。由于所有的 i 和 j 都已事先按照 aa 值排好序, 这样的话只要以双指针的方式在树状数组里揷入点,则对树状数组的揷入操作就能从 O(n2) 次降到 O(n) 次。

通过这样一个算法流程,我们就用 O(nlogn) 的时间处理完了关于第二类点对的信息了。此时算法的时间复杂度 是 T(n)= T( n/2 ) + T( n/2 ) + O( nlogn )= O(nlog2n)。

【三维偏序(陌上开花)-参考代码-CDQ分治】

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int N=200005;
  5. int n,k,m;
  6. struct node
  7. {
  8. int x,y,z,id,w;
  9. bool operator < (const node &A)const{
  10. if(A.x==x && A.y==y) return z < A.z;
  11. else if(A.x==x) return y < A.y;
  12. return x < A.x;
  13. }
  14. }a[N],b[N],d[N];
  15. int ans[N],c[N],cnt[N];
  16. vector <int> v1,v2[N] ;
  17. int lowbit(int x)
  18. {
  19. return x & (-x);
  20. }
  21. void add(int x,int v)
  22. {
  23. for(int i=x;i<N;i+=lowbit(i)) c[i]+=v;
  24. }
  25. int query(int x)
  26. {
  27. int ans=0;
  28. for(int i=x;i;i-=lowbit(i)) ans+=c[i];
  29. return ans;
  30. }
  31. void cdq(int l,int r)
  32. {
  33. if(l==r) return ;
  34. int mid=(l+r)>>1;
  35. cdq(l,mid),cdq(mid+1,r);
  36. int t1=l,t2=mid+1;
  37. for(int i=l;i<=r;i++)
  38. {
  39. if((t1<=mid && a[t1].y<=a[t2].y) || t2>r)
  40. {
  41. add(a[t1].z,a[t1].w);
  42. b[i]=a[t1++];
  43. }
  44. else
  45. {
  46. cnt[a[t2].id]+=query(a[t2].z);
  47. b[i]=a[t2++];
  48. }
  49. }
  50. for(int i=l;i<=mid;i++) add(a[i].z,-a[i].w);
  51. for(int i=l;i<=r;i++) a[i]=b[i];
  52. }
  53. int main()
  54. {
  55. cin>>n>>k;
  56. for(int i=1;i<=n;i++)
  57. {
  58. cin>>a[i].x>>a[i].y>>a[i].z;
  59. a[i].id=i;
  60. }
  61. sort(a+1,a+n+1);
  62. int num=1;
  63. for(int i=2;i<=n+1;i++)
  64. {
  65. if(a[i].x!=a[i-1].x || a[i].y!=a[i-1].y || a[i].z != a[i-1].z)
  66. {
  67. d[++m]=a[i-1];
  68. d[m].w=num;
  69. num=1;
  70. v2[a[i-1].id]=v1;
  71. v1.clear();
  72. }
  73. else
  74. {
  75. num++;
  76. v1.push_back(a[i-1].id) ;
  77. }
  78. }
  79. for(int i=1;i<=m;i++) a[i]=d[i];
  80. cdq(1,m);
  81. for(int i=1;i<=m;i++)
  82. {
  83. int sz=v2[a[i].id].size();
  84. for(auto v:v2[a[i].id]) cnt[v]+=cnt[a[i].id]+sz;
  85. cnt[a[i].id]+=sz;
  86. }
  87. for(int i=1;i<=n;i++) ans[cnt[i]]++;
  88. for(int i=0;i<n;i++) cout<<ans[i]<<'\n';
  89. return 0;
  90. }

 

CDQ分治的限制

  1. 题目允许离线操作
  2. 修改操作对询问的贡献独立,且修改之间互不影响
  3. 修改对答案的贡献是确定的,与判定标准无关

CDQ分治和整体二分

CDQ分治和整体二分都是基于分治的思想,把复杂的问题拆分成许多可以简单求的解子问题。但是这两种算法必须离线处理,不能解决一些强制在线的题目。不过如果题目允许离线的话,这两种算法要比在线解法(如树套树)快很多。

原文链接:https://www.cnblogs.com/wenyutao1/p/17770418.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号