zoukankan      html  css  js  c++  java
  • 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
    ",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
    ",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
    ",d[i]);
        return 0;
    }
    

    总的来说,CDQ分治是可以学的!!!

    转载需注明出处。
  • 相关阅读:
    10-3 集合之Set
    【Angular】排序
    【Mongous】
    【验证码】
    爬虫
    【DOM】
    年月日
    【Mocha】
    【Test】
    洛谷——P1823 音乐会的等待
  • 原文地址:https://www.cnblogs.com/jz929/p/11817820.html
Copyright © 2011-2022 走看看