CDQ分治總結【總結很辛苦的~】
阿新 • • 發佈:2019-01-01
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分治是可以學的!!!