CDQ分治总结【总结很辛苦的~】

版权声明:嘻嘻,要转的话请告知一声~ https://blog.csdn.net/Larry1118/article/details/85239260

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分治是可以学的!!!

猜你喜欢

转载自blog.csdn.net/Larry1118/article/details/85239260
今日推荐