1. 程式人生 > 其它 >【cdq分治】【模板】

【cdq分治】【模板】

Problem

三維偏序的模板題。

Solution:

經典做法是,先排序控制一維,再用cdq分治控制一維,最後用資料結構(通常是樹狀陣列)維護一維。

cdq分治還可以巢狀,每巢狀一次,複雜度乘一個log,可以拓展到n維偏序(但超過3維,效率就很低了,通常會使用kd-tree),因此,這題也可以用兩個cdq分治巢狀解決(但我不會)

複雜度O(n log^2 n),cdq分治和樹狀陣列各提供一個log

需要注意的問題:

在cdq分治的外層排序中,必須要滿足在cdq內部劃分時,右半區間不能對左半區間造成影響,因此,當兩個元素完全相同時,需要把他們合成一個元素,最後再累計相同元素的貢獻。

Code

#include<bits/stdc++.h>
using namespace std;

inline int read()
{
	register int x=0,w=1;
	register char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-') {ch=getchar();w=-1;}
	while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
inline void write(int x)
{
	if(x<0) {putchar('-');x=~(x-1);}
	if(x>9) write(x/10);
	putchar('0'+x%10);
}
const int N=1e5+100;
int n,k;
struct node{
	int a,b,c,s,ans;
	bool operator<(const node&x)const{
	     if(a!=x.a) return a<x.a;
	     if(b==x.b) return c<x.c;
	     return b<x.b;
	}
	bool operator==(const node&x)const{
	     return a==x.a&&b==x.b&&c==x.c;
	}
}a[N];
int t[N<<1];
int cnt;
bool cmp(node x,node y)
{
	return x.b<y.b;
}
void add(int x,int c)
{
	for(int i=x;i<=k;i+=i&-i)
	t[i]+=c;
}
int ask(int x)
{
	int res=0;
	for(int i=x;i;i-=i&-i)
	res+=t[i];
	return res;
}
void cdq(int l,int r)
{
	if(l==r) return;
	int mid=l+r>>1;
	cdq(l,mid);cdq(mid+1,r);
	sort(a+l,a+mid+1,cmp);sort(a+mid+1,a+r+1,cmp);
	int pos=l;
	for(int i=mid+1;i<=r;++i)
	{
		while(pos<=mid&&a[pos].b<=a[i].b) {
			add(a[pos].c,a[pos].s);pos++;
		}
		a[i].ans+=ask(a[i].c);
	}
	for(int i=l;i<pos;++i) add(a[i].c,-a[i].s);
}
int d[N];
int main()
{
    n=read();k=read();
    for(int i=1;i<=n;++i){
    	a[i].a=read();
    	a[i].b=read();
    	a[i].c=read();
    	a[i].s=1;
	}
	sort(a+1,a+n+1);
//	int cnt=0;
	for(int i=1;i<=n;++i)
	if(a[i]==a[cnt]) a[cnt].s++;
	else a[++cnt]=a[i];
	cdq(1,cnt);
	for(int i=1;i<=cnt;++i)
	d[a[i].ans+a[i].s-1]+=a[i].s;
	for(int i=0;i<n;++i){
		write(d[i]);puts("");
	}
	return 0;
}

cdq分治的遞迴函式跟歸併排序十分類似,實際上,我們可以把cdq中的sort改為歸併排序,從而減少常數。

常數優化版本

#include<bits/stdc++.h>
using namespace std;

inline int read()
{
	register int x=0,w=1;
	register char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if(ch=='-') {ch=getchar();w=-1;}
	while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*w;
}
inline void write(int x)
{
	if(x<0) {putchar('-');x=~(x-1);}
	if(x>9) write(x/10);
	putchar('0'+x%10);
}
const int N=1e5+100;
int n,k;
struct node{
	int a,b,c,s,ans;
	bool operator<(const node&x)const{
	     if(a!=x.a) return a<x.a;
	     if(b==x.b) return c<x.c;
	     return b<x.b;
	}
	bool operator==(const node&x)const{
	     return a==x.a&&b==x.b&&c==x.c;
	}
}a[N],tmp[N];
int t[N<<1];
int cnt;
bool cmp(node x,node y)
{
	return x.b<y.b;
}
void add(int x,int c)
{
	for(int i=x;i<=k;i+=i&-i)
	t[i]+=c;
}
int ask(int x)
{
	int res=0;
	for(int i=x;i;i-=i&-i)
	res+=t[i];
	return res;
}
void cdq(int l,int r)
{
	if(l==r) return;
	int mid=l+r>>1;
	cdq(l,mid);cdq(mid+1,r);
//	sort(a+l,a+mid+1,cmp);sort(a+mid+1,a+r+1,cmp);
	int pos=l;
	for(int i=mid+1;i<=r;++i)
	{
		while(pos<=mid&&a[pos].b<=a[i].b) {
			add(a[pos].c,a[pos].s);pos++;
		}
		a[i].ans+=ask(a[i].c);
	}
	for(int i=l;i<pos;++i) add(a[i].c,-a[i].s);
	pos=l-1;
	int p=l,q=mid+1;
	while(p<=mid||q<=r)
	{
		if(p>mid)
		tmp[++pos]=a[q++];
		else if(q>r) tmp[++pos]=a[p++];
		else tmp[++pos]=a[a[p].b<a[q].b?p++:q++];
	}
	for(int i=l;i<=r;++i) a[i]=tmp[i];
}
int d[N];
int main()
{
    n=read();k=read();
    for(int i=1;i<=n;++i){
    	a[i].a=read();
    	a[i].b=read();
    	a[i].c=read();
    	a[i].s=1;
	}
	sort(a+1,a+n+1);
//	int cnt=0;
	for(int i=1;i<=n;++i)
	if(a[i]==a[cnt]) a[cnt].s++;
	else a[++cnt]=a[i];
	cdq(1,cnt);
	for(int i=1;i<=cnt;++i)
	d[a[i].ans+a[i].s-1]+=a[i].s;
	for(int i=0;i<n;++i){
		write(d[i]);puts("");
	}
	return 0;
}