【学习笔记】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,相当于左边排序建凸壳,回答右半边

几道例题

posted @ 2025-12-26 09:58  Ming3398  阅读(27)  评论(0)    收藏  举报