1. 程式人生 > >CDQ分治總結【總結很辛苦的~】

CDQ分治總結【總結很辛苦的~】

CDQ這個東西嘛,說容易其實也很容易,說難其實也有些難,但只要細細品味,定能發現其中的真理的!那真理,也會像蝴蝶一般,破蛹而出,化身為一道亮麗的風景線。 ——題記。
咳咳,閒話就講到這裡了,切入正題。
首先我們來了解一下CDQ分治這個東東。
CDQ分治,他的常數小,但必須離線操作 the most important!
然後,講講CDQ的答題思路。

first,。將[l,r]分成[l,mid]和[mid+1,r](mid=l+r>>1)
then,。進入兩區間,直至l==r才return
after that,。求[l,mid]區間對[mid+1,r]的貢獻。

OK,下面來講講例題,這樣才能懂吧。。。
1:裸的CDQ——逆序對統計(一維偏序)

給出n,和長為n的序列,讓你求著逆序對個數。
sample input:
3
1 2 3
sample output:
0
100%:n<=5*10^5

這題很簡單吧,樹狀陣列或是歸併排序(就是CDQ分治的基本使用)。

先來個樹狀陣列的:

#include<cstdio>
#include<algorithm>
#define ll long long
#define lowbit(x) x&-x
#define N 500010
using namespace std;
struct node{ll val,num;}a[N]; ll ans=0,t[N]={0},c[N];int n,tot=1; inline int read() { int x=0,f=1; char c=getchar(); while(c<'0' || c>'9') f=(c=='-') ? -1:f,c=getchar(); while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int cmp(node x,
node y) {return x.val<y.val;} void add(int x) {for (;x<=n;x+=lowbit(x)) t[x]++;} ll total(int x) {ll s=0; for (;x>0;x-=lowbit(x)) s+=t[x]; return s;} int main() { n=read(); for (int i=1;i<=n;i++) a[i]=(node){read(),i}; sort(a+1,a+n+1,cmp);c[a[1].num]=1; for (int i=2;i<=n;i++) { if (a[i].val!=a[i-1].val) tot++; c[a[i].num]=tot; } for (int i=n;i>0;i--) add(c[i]),ans+=total(c[i]-1); printf("%lld\n",ans); return 0; }

再來一段CDQ分治(也就是歸併)的。

#include<cstdio>
using namespace std;
int n,a[500010],b[500010];
long long ans=0;

inline int read()
{
	int x=0; char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

void CDQ(int l,int r)
{
	if (l==r) return;
	int mid=l+r>>1;
	CDQ(l,mid),CDQ(mid+1,r);
	int le=l,ri=mid+1,now=l;
	while (le<=mid && ri<=r)
	{
		if (a[le]<=a[ri] && le<=mid) b[now++]=a[le++];
		else ans+=mid-le+1,b[now++]=a[ri++];
	}
	while (le<=mid) b[now++]=a[le++];
	while (ri<=r) b[now++]=a[ri++];
	for (int i=l;i<=r;i++) a[i]=b[i];
}

int main()
{
	n=read();
	for (int i=1;i<=n;i++) a[i]=read();
	CDQ(1,n);
	printf("%lld\n",ans); 
	return 0;
}

2:裸的CDQ——二維偏序

給出n,以及n對(x,y)
求對於每一對(x,y)共有多少(x1,y1)滿足x>=x1&y>=y1
n<=10^5

真的,這道題其實也很簡單的。
一維排序,另一維CDQ分治即可。

#include<cstdio>
#include<algorithm>
using namespace std;
struct node{int x,y,fr;}a[100010],b[100010];
int n,c[100010];

inline int read()
{
	int x=0; char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

int cmp(node x,node y) {return x.x<y.x;}

void CDQ(int l,int r)
{
	if (l==r) return;
	int mid=l+r>>1;
	CDQ(l,mid),CDQ(mid+1,r);
	int le=l,ri=mid+1,now=l;
	while (le<=mid && ri<=r)
	{
		if (a[le].y<a[ri].y) b[now++]=a[le++];
		if (a[le].y>a[ri].y) c[a[ri].fr]+=mid-le+1,b[now++]=a[ri++];
		else b[now++]=a[le++];
	}
	while (le<=mid) b[now++]=a[le++];
	while (ri<=r) b[now++]=a[ri++];
	for (int i=l;i<=r;i++) a[i]=b[i];
}

int main()
{
	n=read();
	for (int i=1;i<=n;i++)
		a[i].x=read(),a[i].y=read(),a[i].fr=i;
	sort(a+1,a+n+1,cmp);
	CDQ(1,n);
	for (int i=1;i<=n;i++)
		printf("%d ",c[i]);
	return 0;
}

3:裸的CDQ——陌上花開(三維偏序)
在這裡插入圖片描述
本題洛谷上有,可自己做一下。
對於這題,我們無語。。。。。。
自己手賤,打了個CDQ套CDQ。。。
一維排序,另外兩維就兩個CDQ即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100010
using namespace std;
int n,k,ans[maxn],d[maxn];
struct node{int x,y,z,*ans;bool b;
    bool operator==(const node &a)
    const {return x==a.x&&y==a.y&&z==a.z;}
}a[maxn],b[maxn],c[maxn];
inline bool cmp(const node &a,const node &b) 
{
	return a.x==b.x ? (a.y==b.y ? a.z<b.z:a.y<b.y):a.x<b.x;
}

inline int read()
{
	int x=0; char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

void CDQ2(int l,int r)
{
	if (l==r) return;
	int mid=l+r>>1;
	CDQ2(l,mid),CDQ2(mid+1,r);
	for (int i=l,j=l,k=mid+1,cnt=0;i<=r;i++)
		if ((k>r || b[j].z<=b[k].z) && j<=mid) c[i]=b[j++],cnt+=c[i].b;
		else c[i]=b[k++],*c[i].ans+=cnt*(!c[i].b);
	for (int i=l;i<=r;i++) b[i]=c[i];
}

void CDQ(int l,int r)
{
	if (l==r) return;
	int mid=l+r>>1;
	CDQ(l,mid),CDQ(mid+1,r);
	for (int i=l,j=l,k=mid+1;i<=r;i++)
		if ((k>r || a[j].y<=a[k].y) && j<=mid) b[i]=a[j++],b[i].b=1;
		else b[i]=a[k++],b[i].b=0;
	for (int i=l;i<=r;i++) a[i]=b[i];
	CDQ2(l,r);
}

int main()
{
    n=read(),k=read();
    for (int i=1;i<=n;i++)
    	a[i].x=read(),a[i].y=read(),a[i].z=read(),a[i].ans=&ans[i],ans[i]=0;
    sort(a+1,a+n+1,cmp);
    for (int i=n-1;i>0;i--)
    	if (a[i]==a[i+1]) *a[i].ans=*a[i+1].ans+1;
    CDQ(1,n);
    for (int i=1;i<=n;i++) ++d[ans[i]];
    for (int i=0;i<n;i++) printf("%d\n",d[i]);
    return 0;
}

總的來說,CDQ分治是可以學的!!!