CDQ分治学习笔记
作者:互联网
CDQ 分治
\(CDQ\) 分治可以用来解决多维偏序问题
它是一个在线算法
二维偏序
给你 \(n\) 个元素,每个元素有两个属性 \(a_i\) 和 \(b_i\),定义 \(f(i)\) 表示 \(a_j\le a_i\) 且 \(b_j\le b_i\) 的元素数量
求 \(f(i)=d\) 的数量 \((d\in[0,n])\)
思路:
我们可以以 \(a_i\) 为第一关键字,\(b_i\) 为第二关键字从小到大排序
排序后对于第 \(i\) 个数我们找前 \(i-1\) 个数中 \(b_j\le b_i\) 的 \(j\) 的个数
搞一个树状数组维护即可,复杂度 \(O(n\log n)\)
三维偏序
三维偏序的问题无非就是在二维偏序上加了一维 \(c\)
我们考虑先按第一维 \(a\) 排序
然后第二维考虑归并排序,第三维用树状数组
我们在归并排序的时候考虑 \([l,mid]\) 对 \([mid+1,r]\) 的贡献
由于我们已经对第一维 \(a\) 排序过了,因此归并排序时无论 \(a\) 怎样被打乱,\([mid+1,r]\) 中所有元素的 \(a\) 值是不小于 \([l,mid]\) 的,第二维是 \(ok\) 的
在满足前两维均有序的条件下,我们可以用类似于二维偏序的解法搞一个树状数组统计就行了
这样的时间复杂度是 \(O(n\log^2 n)\) 的
\(p.s.\) 要对元素进行去重
code:
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&-x
const int N=1e5+5;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,m;
int ans[N],c[N];
struct num{
int a,b,c,w,f;
inline bool operator < (const num A){
return a==A.a?(b==A.b?c<A.c:b<A.b):a<A.a;
}
}a[N],tmp[N];
inline void add(int x,int k){
for(;x<=m;x+=lowbit(x))
c[x]+=k;
}
inline int query(int x){
int res=0;
for(;x;x-=lowbit(x)) res+=c[x];
return res;
}
inline 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,cnt=l-1;
while(i<=mid&&j<=r){
if(a[i].b<=a[j].b) add(a[i].c,a[i].w),tmp[++cnt]=a[i++];
else a[j].f+=query(a[j].c),tmp[++cnt]=a[j++];
}
while(i<=mid) add(a[i].c,a[i].w),tmp[++cnt]=a[i++];
while(j<=r) a[j].f+=query(a[j].c),tmp[++cnt]=a[j++];
for(i=l;i<=mid;++i) add(a[i].c,-a[i].w);
for(i=l;i<=r;++i) a[i]=tmp[i];
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;++i)
a[i].a=read(),a[i].b=read(),a[i].c=read(),a[i].w=1;
sort(a+1,a+n+1);
int r=1;
for(int i=2;i<=n;++i){
if(a[i].a==a[r].a&&a[i].b==a[r].b&&a[i].c==a[r].c) ++a[r].w;
else a[++r]=a[i];
}
CDQ(1,r);
for(int i=1;i<=r;++i) ans[a[i].f+a[i].w-1]+=a[i].w;
for(int i=0;i<n;++i) cout<<ans[i]<<endl;
}
标签:偏序,ch,int,分治,mid,笔记,CDQ,排序 来源: https://www.cnblogs.com/into-qwq/p/16485156.html