专题:CDQ 分治

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

简介

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

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

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

解决和点对有关的问题

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

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

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

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

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

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

分析:

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

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

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

#include <bits/stdc++.h>
using namespace std; typedef long long ll;
const int N=200005;
int n,k,m; struct node
{
int x,y,z,id,w;
bool operator < (const node &A)const{
if(A.x==x && A.y==y) return z < A.z;
else if(A.x==x) return y < A.y;
return x < A.x;
}
}a[N],b[N],d[N]; int ans[N],c[N],cnt[N];
vector <int> v1,v2[N] ; int lowbit(int x)
{
return x & (-x);
} void add(int x,int v)
{
for(int i=x;i<N;i+=lowbit(i)) c[i]+=v;
} int query(int x)
{
int ans=0;
for(int i=x;i;i-=lowbit(i)) ans+=c[i];
return ans;
} void cdq(int l,int r)
{
if(l==r) return ;
int mid=(l+r)>>1;
cdq(l,mid),cdq(mid+1,r);
int t1=l,t2=mid+1;
for(int i=l;i<=r;i++)
{
if((t1<=mid && a[t1].y<=a[t2].y) || t2>r)
{
add(a[t1].z,a[t1].w);
b[i]=a[t1++];
}
else
{
cnt[a[t2].id]+=query(a[t2].z);
b[i]=a[t2++];
}
}
for(int i=l;i<=mid;i++) add(a[i].z,-a[i].w);
for(int i=l;i<=r;i++) a[i]=b[i];
} int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i].x>>a[i].y>>a[i].z;
a[i].id=i;
}
sort(a+1,a+n+1);
int num=1;
for(int i=2;i<=n+1;i++)
{
if(a[i].x!=a[i-1].x || a[i].y!=a[i-1].y || a[i].z != a[i-1].z)
{
d[++m]=a[i-1];
d[m].w=num;
num=1;
v2[a[i-1].id]=v1;
v1.clear();
}
else
{
num++;
v1.push_back(a[i-1].id) ;
}
}
for(int i=1;i<=m;i++) a[i]=d[i];
cdq(1,m);
for(int i=1;i<=m;i++)
{
int sz=v2[a[i].id].size();
for(auto v:v2[a[i].id]) cnt[v]+=cnt[a[i].id]+sz;
cnt[a[i].id]+=sz;
}
for(int i=1;i<=n;i++) ans[cnt[i]]++;
for(int i=0;i<n;i++) cout<<ans[i]<<'\n';
return 0;
}

CDQ分治的限制

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

CDQ分治和整体二分

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

CDQ分治和三维偏序的更多相关文章

  1. 并不对劲的cdq分治解三维偏序

    为了反驳隔壁很对劲的太刀流,并不对劲的片手流决定与之针锋相对,先一步发表cdq分治解三维偏序. 很对劲的太刀流在这里->  参照一.二维偏序的方法,会发现一位偏序就是直接排序,可以看成通过排序使 ...

  2. cdq分治解决三维偏序

    问题背景 在三维坐标系中有n个点,坐标为(xi,yi,zi). 定义一个点A比一个点B小,当且仅当xA<=xB,yA<=yB,zA<=zB.问对于每个点,有多少个点比它小.(n< ...

  3. 【算法学习】【洛谷】cdq分治 & P3810 三维偏序

    cdq是何许人也?请参看这篇:https://wenku.baidu.com/view/3b913556fd0a79563d1e7245.html. 在这篇论文中,cdq提出了对修改/询问型问题(Mo ...

  4. hdu5618(cdq分治求三维偏序)

    题意:给n(n<=100000)个点,坐标为(xi,yi,zi)(1<=xi,yi,zi<=100000),定义一个点A比一个点B小,当且仅当xA<=xB,yA<=yB, ...

  5. SPOJ:Another Longest Increasing Subsequence Problem(CDQ分治求三维偏序)

    Given a sequence of N pairs of integers, find the length of the longest increasing subsequence of it ...

  6. BZOJ 3262: 陌上花开 (cdq分治,三维偏序)

    #include <iostream> #include <stdio.h> #include <algorithm> using namespace std; c ...

  7. HDU4742 CDQ分治,三维LIS

    HDU4742 CDQ分治,三维LIS 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4742 题意: 每个球都有三个属性值x,y,z,要求最长的lis的 ...

  8. CDQ解决一些三维偏序的问题

    本来几天前就该记录的东西,硬生生被我拖到了现在,太懒了... 在cdq学习时,二维偏序已经解决了,无非就是先sort使第一维有序,然后再用cdq或者数据结构处理第二维.而三维偏序的时候呢,大佬的做法好 ...

  9. CDQ分治(三维偏序集)

    排序,三关键字 去重 归并排序+树状数组 #include<bits/stdc++.h> using namespace std; #define re register int cons ...

  10. BZOJ 3295:[Cqoi2011]动态逆序对(三维偏序 CDQ分治+树状数组)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3295 题意:简单明了. 思路:终于好像有点明白CDQ分治处理三维偏序了.把删除操作看作是插入操作,那 ...

随机推荐

  1. elementui中deep的用法/deep/

    在做第一个vue项目时遇到了一个问题,在使用elementui中的Drawer(抽屉组件时),想要修改Drawer的标题,尝试着修改title的样式,但没有效果,尝试了很多方法,最后用/deep/解决 ...

  2. 调用内部或私有方法的N种方法

    非公开的类型或者方法被"隐藏"在程序集内部,本就不希望从外部访问,但是有时候调用一个内部或者私有方法可能是唯一的"救命稻草",这篇文章列出了几种具体的实现方式. ...

  3. Button按钮:得到鼠标焦点后自动放大,失去鼠标焦点后自动缩小_

    作用 程序设计过程中,我们经常需要增加一些动态效果,以此改善用户的使用体验.本文将介绍一种方法,动态显示按钮状态,使其得到鼠标焦点后自动放大,失去鼠标焦点后自动缩小. 效果图 先放一张原图(鼠标还未移 ...

  4. Hexo博客Next主题阅读次数热度不能读取的问题,报错Counter not initialized! More info at console err msg.

    加入valine在线评论 设置效果: 设置方法: 首先要先去LeanCloud注册一个帐号.然后再创建一个应用. 拿到appid和appkey之后,打开themes/next/_config.yml主 ...

  5. .NET Core多线程 (1) Thread与Task

    去年换工作时系统复习了一下.NET Core多线程相关专题,学习了一线码农老哥的<.NET 5多线程编程实战>课程,我将复习的知识进行了总结形成本专题.同时也特别推荐有兴趣的读者去学习一线 ...

  6. Win11 开启 telnet

    'telnet' 不是内部或外部命令,也不是可运行的程序或批处理文件. 首先我们打开底部"开始菜单",如图所示.接着打开其中的"设置". 然后进入左边的&quo ...

  7. P1941 [NOIP2014 提高组] 飞扬的小鸟 题解

    我们先不管障碍物. 设 \(f[i][j]\) 表示来到点 \((i,j)\) 的最少点击屏幕数. 因为每秒要不上升 \(k\times x[i]\),要么下降 \(y[i]\). 所以有: \[f[ ...

  8. 从一道面试题来谈谈Golang中的 ==

    写这篇文章的时候,已经离我找工作有一段时间了,但是觉得这道题不管是面试还是日常的工作中,都会经常遇到,所以还是特意写一篇文章,记录下自己对Golang中==的理解.如文章中出现不对的地方,请不吝赐教, ...

  9. 简述redis的单线程模式

    前言 在redis版本6之前,网络IO和键值对读写都是由一个线程来完成的.而redis的其他功能,比如持久化.异步删除.集群数据同步等,是由其他线程完成的. 为什么采用单线程 多线程有助于提升吞吐率( ...

  10. .NET爬取美图官网首页数据实战

    前言: 在当今信息化社会,网络数据分析越来越受到重视.而作为开发人员,掌握一门能够抓取网页内容的语言显得尤为重要.在此篇文章中,将分享如何使用 .NET构建网络抓取工具.详细了解如何执行 HTTP 请 ...