【学习笔记】CDQ分治
参考
用于求解以下三类问题的思想
- 点对问题(偏序)
- 1D1D优化(基于偏序)
- 在线问题转离线
原理:解决左半边,处理左半边对右半边的影响,解决右半边,类似于中序遍历,其实就是用递归的方式顺序扫描
点对问题
三维偏序
第一维排序,第二维CDQ,第三维数据结构
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;int K;
struct node{
int a,b,c,v;
int ans;
node(){
a=b=c=v=ans=0;
}
const bool operator!=(const node y)const{
return (a!=y.a)||(b!=y.b)||(c!=y.c);
}
}q[N];
int tree[N];
void update(int x,int d){
while(x<=K){
tree[x]+=d;
x+=x&-x;
}
}
int query(int x){
int ans=0;
while(x){
ans+=tree[x];
x-=x&-x;
}
return ans;
}
bool cmpa(node x,node y){
if(x.a!=y.a) return x.a<y.a;
if(x.b!=y.b) return x.b<y.b;
return x.c<y.c;
}
bool cmpb(node x,node y){
return x.b<y.b;
}
void CDQ(int l,int r){
if(l==r) return ;
int mid=l+r>>1;
CDQ(l,mid);CDQ(mid+1,r);
sort(q+l,q+1+mid,cmpb);sort(q+mid+1,q+1+r,cmpb);
int i=l,j=mid+1;
while(j<=r){
while(i<=mid&&q[i].b<=q[j].b){
update(q[i].c,q[i].v);
i++;
}
q[j].ans+=query(q[j].c);
j++;
}
for(int k=l;k<i;k++) update(q[k].c,-q[k].v);
}
int ans[N];
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n;cin>>n>>K;
for(int i=1;i<=n;i++) cin>>q[i].a>>q[i].b>>q[i].c;
sort(q+1,q+1+n,cmpa);
int w=0,idx=0;
for(int i=1;i<=n;i++){
w++;
if(q[i]!=q[i+1]){
q[++idx]=q[i];
q[idx].v=w;
w=0;
}
}
CDQ(1,idx);
for(int i=1;i<=idx;i++) ans[q[i].ans+q[i].v-1]+=q[i].v;
for(int i=0;i<n;i++) cout<<ans[i]<<"\n";
return 0;
}
四维偏序
CDQ套CDQ
高维偏序
以此类推,不过复杂度很劣,一般不用CDQ求解
1D1D优化
转移方程满足偏序关系,可用CDQ离线优化
在线问题转离线
解决左半边
左对右影响:左半边离线操作,右半边离线回答询问
解决右半边
应用:斜率优化dp,相当于左边排序建凸壳,回答右半边
几道例题

浙公网安备 33010602011771号