CDQ分治

CDQ分治

什么是CDQ分治?

CDQ分治是一种基于时间(其实不一定)的分治,最基本的运用是三位偏序。

三位偏序问题 [BZOJ3262]陌上花开

对于这个问题,我们先对点去重。

以第一维(花形)为第一关键字,第二维(颜色)为第二关键字,第三维(气味)为第三关键字,从小到大将点排序。

分治,回溯时处理答案

回溯时,将两个子序列以第二维(颜色)为第一关键字,第三维(气味)为第二关键字分别排序

通过左子序列更新右子序列的答案,记录两个下标\(j~|~j\epsilon[left,mid]\)\(i~|~i\epsilon[mid+1,right]\),从左向右移动,使得点\(i\)的颜色值(第二维)始终比点\(j\)的大。

如此一来,点\(i\)的花形值(第一维)始终比点\(k~|~k\epsilon[left,j]\)的大,点\(i\)的颜色值(第二维)始终比点\(k~|~k\epsilon[left,j]\)的大,所以只用求\(\sum_{k=left}^{j}k_{第三维}≤i_{第三维}\)的值即可,显然可以通过建立树状数组来存储。

由于每次都是通过左子序列更新右子序列的答案,所以一定不会有遗漏

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#define maxn 100000
#define maxk 200000
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
void read(int &x){
	int f=1;x=0;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=x*10+ch-'0';
		ch=getchar();
	}
	x*=f;
}
int n,k;
struct BIT{									//树状数组
	int tree[maxk+5];
	int lowbit(int x){return x&-x;}
	void update(int x,int v){
		for(int i=x;i<=k;i+=lowbit(i))
			tree[i]+=v;
	}
	int query(int x){
		int ans=0;
		for(int i=x;i>=1;i-=lowbit(i)) ans+=tree[i];
		return ans;
	}
} Tr;
struct node{
	int x,y,z,i,cnt,ans;
	node(){x=y=z=i=cnt=ans=0;}
	friend bool operator==(node a,node b){return a.x==b.x&&a.y==b.y&&a.z==b.z;}
	friend bool operator!=(node a,node b){return !(a==b);}
} A[maxn+5],w[maxn+5],s1[maxn+5];
int cnt=0;
bool cmp1(node a,node b){return a.x!=b.x?a.x<b.x:(a.y!=b.y?a.y<b.y:a.z<b.z);}
bool cmp2(node a,node b){return a.y!=b.y?a.y<b.y:a.z<b.z;}
void merge(node s[],int left,int right){
	int mid=(left+right)/2;
	int i=left,j=mid+1;
	for(int k=left;k<=right;k++){
		if(j>right||(i<=mid&&cmp2(s[i],s[j]))) s1[k]=s[i++];
		else s1[k]=s[j++];
	}
	for(int k=left;k<=right;k++) s[k]=s1[k];
}
void uniq(node s1[],node s2[],int cnt1,int &cnt2){
	cnt2=0;
	for(int i=1;i<=cnt1;i++){
		if(i==1||s1[i]!=s1[i-1]) s2[++cnt2]=s1[i];
		else s2[cnt2].cnt+=s1[i].cnt;
	}
}
void CDQ(int l,int r){
	if(l==r) return;
	int mid=(l+r)/2;
	CDQ(l,mid),CDQ(mid+1,r);
	merge(w,l,mid),merge(w,mid+1,r);		//归并排序
	int j=l-1;
	for(int i=mid+1;i<=r;i++){
		while(j<mid&&w[j+1].y<=w[i].y)
			j++,Tr.update(w[j].z,w[j].cnt);
		w[i].ans+=Tr.query(w[i].z);
	}
	for(int i=l;i<=j;i++)
		Tr.update(w[i].z,-w[i].cnt);
}
int Ans[maxn+5];
int main(){
	read(n),read(k);
	for(int i=1;i<=n;i++) read(A[i].x),read(A[i].y),read(A[i].z),A[i].i=i,A[i].cnt=1;
	sort(A+1,A+n+1,cmp1),uniq(A,w,n,cnt);	//去重
	CDQ(1,cnt),sort(w+1,w+cnt+1,cmp1);
	for(int i=1;i<=cnt;i++) Ans[w[i].ans+w[i].cnt-1]+=w[i].cnt;
	for(int i=0;i<n;i++) printf("%d\n",Ans[i]);
}
posted @ 2019-08-31 13:14  OIER_Yu  阅读(131)  评论(0编辑  收藏  举报