【数据结构】CDQ分治初步
目录
原理
代码
原理
模板题传送门:https://www.acwing.com/problem/content/2817/
cdq分治用来解决什么样的问题呢?一般来说可以:
- 统计具有三维属性 () 的、满足一定的比较关系 有多少对。
- 优化一些数据结构,算法。
流程:
下面说一下cdq分治解决上面模板题的流程。
排序解决第一维情况
首先我们将整个 集排序。(以 为第一关键字,以 为第二关键字,以 为第三关键字)
这样做便可以处理掉第一维的情况。
接下来,我们采取分治的思想来统计满足题意的对数。
对于目前需要处理的区间 :
记 ,左区间 ,右区间 。
分情况讨论:
- 假如满足条件的点对 都在左区间或者右区间,那么我们递归处理下去就好。
- 否则, 必然在左区间, 必然在右区间,我们对这样的点对进行统计。
故重点在于第二种情况:
此时第一维情况()必然是满足的。
采取双指针解决第二维情况
假设现在的左右两个区间是按照 为关键字排好序了,我们记左区间的指针为 ,右区间的指针为 。
因为我们的递归是自下而上的(基于归并排序的思想),所以我们可以在处理当前区间时进行以 为关键字的排序,这样就可以保证处理到每一个区间时都是以 为关键字的排序的了。
对于每一个 ,我们让 右移,直到找到第一个 ,满足 ,那么对于 均有 。
采用树状数组解决第三维情况
最后,只要解决第三维情况,就可以统计出每一个 相应的贡献(对数)了:对于元素 其中 ,我们将这样的 的属性 值放入树状数组中,只需查询一下 就可以找到满足 的个数了,注意到与此同时,前面两维情况也同时被满足,所以这样的个数就是所求的贡献。
细节:
我们记三维数据同时相等的 为同一类,发现分治的时候同一类之间并不好处理,所以我们将同一类进行合并,用 来记录每一类的个数,那么对不同类之间进行分治就可以了。
代码
#pragma GCC optimize("O3")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+5, M=2e5+5;
int n,m;
struct data{
int a,b,c,cnt,res;
bool operator<(const data &o)const{
if(a!=o.a) return a<o.a;
if(b!=o.b) return b<o.b;
return c<o.c;
}
bool operator==(const data &o)const{
return a==o.a && b==o.b && c==o.c;
}
}e[N], tmp[N];
int tot;
int tr[M];
int lowbit(int x){return x&-x;}
void add(int p,int k){
for(;p<M;p+=lowbit(p)) tr[p]+=k;
}
int query(int p){
int res=0;
for(;p;p-=lowbit(p)) res+=tr[p];
return res;
}
void cdq(int l,int r){
if(l>=r) return;
int mid=l+r>>1;
cdq(l,mid), cdq(mid+1,r);
// 这里定义左区间对应的指针为 j, 右区间对应的指针为 i.
for(int j=l, i=mid+1, k=l;k<=r;k++)
if(i>r || j<=mid && e[j].b<=e[i].b) add(e[j].c,e[j].cnt), tmp[k]=e[j++]; // 如果说右区间的指针已经走到边界,或者左区间的b值比较小。
else e[i].res+=query(e[i].c), tmp[k]=e[i++];
for(int j=l;j<=mid;j++) add(e[j].c,-e[j].cnt); // 恢复桶
for(int k=l;k<=r;k++) e[k]=tmp[k]; // 完成排序,复制
}
int ans[M];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int a,b,c; cin>>a>>b>>c;
e[i]={a,b,c,1};
}
sort(e+1,e+1+n);
for(int i=1;i<=n;i++)
if(e[i]==e[tot]) e[tot].cnt++;
else e[++tot]=e[i];
cdq(1,tot);
for(int i=1;i<=tot;i++) ans[e[i].res+e[i].cnt-1]+=e[i].cnt;
for(int i=0;i<n;i++) cout<<ans[i]<<'\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】