CDQ分治p1357

  多方查找找到了2008年陈丹琪引入CDQ分治的 从《Cash》谈一类分治算法的应用.doc ,CDQ分治的名字由来也是她.

  什么叫CDQ分治呢?来看一道二维数点题p1357.

  

  看了一眼题,我会树状数组!

  现在拿它来引入CDQ分治.先全部按照x排序,对于区间[l,r]内的贡献都可以分四步进行.

  1.算区间[l,mid]的贡献并把[l,mid]排序

  2.算区间[mid+1,r]的贡献并把[mid+1,r]排序

  3.算[l,mid]对于[mid+1,r]的贡献

  4.把[l,r]排序.

  删去算贡献的部分,是不是很像归并排序?基于归并排序的思想,[l,mid]和[mid+1,r]内部是有序的,我们可以O(r-l)的进行[l,r]的排序以及算贡献的过程.

  那啥,为了防止有些人不懂,先放一个nlog^2n的算法

bool Orz(node a,node b)
{
    return a.x<b.x;
}
void CDQ(int l,int r)
{
    if(l==r)
        return ;//自己对自己当然没贡献了
    int mid=(l+r)/2;
    CDQ(l,mid);
    CDQ(mid+1,r);
    //简陋的树状数组实现贡献统计
    for(int i=l;i<=mid;i++)//怎么还用树状数组啊?
        add(o[i].y,1);
    for(int i=mid+1;i<=r;i++)
        ans[o[i].i]+=ask(o[i].y);//算贡献
    for(int i=l;i<=mid;i++)//怎么又减回去了啊?
        add(o[i].y,-1);
    sort(o+l,o+r+1);//简陋的sort排序
}
int main()
{
    freopen("123.in","r",stdin);
    n=read();
    for(i=1;i<=n;i++)
    {
        o[i].x=read();
        o[i].y=read();
        o[i].i=i;//记录原始位置
    }
    sort(o+1,o+1+n,Orz);
    CDQ(1,n);//调用分治
    for(i=1;i<=n;i++)
        cout<<ans[i]<<endl;
}
简陋的CDQ分治

  上面的简陋的算法的log是完全可以省掉的,就像归并排序统计逆序对一样,那个树状数组实现的统计贡献完全可以O(l-r)实现.

  不会有人连归并排序都不会吧?

struct node
{
    int x,y;
    int i;
}o[60010],temp[60010];
int ans[60010];
int i,n;
inline bool Orz(node a,node b)
{
    return a.x==b.x?a.y<b.y:a.x<b.x;
}
inline void CDQ(int l,int r)
{
    if(l==r)
        return ;//自己对自己当然没贡献了
    int mid=(l+r)/2;
    CDQ(l,mid);
    CDQ(mid+1,r);
    int a=l,b=mid+1,tot=l;
    for(;a<=mid&&b<=r;tot++)//合并区间并统计答案
    {
        if(o[a].y<=o[b].y)
        {
            temp[tot]=o[a];
            a++;
        }
        else 
        {
            ans[o[b].i]+=a-l;//统计答案
            temp[tot]=o[b];
            b++;
        }
    }
    while(a<=mid)
    {
        temp[tot]=o[a];
        a++;tot++;
    }
    while(b<=r)
    {
        ans[o[b].i]+=a-l;//等价于mid+l-1
        temp[tot]=o[b];
        b++;tot++;
    }
    for(int i=l;i<=r;i++)
        o[i]=temp[i];
}
int main()
{
    n=read();
    for(i=1;i<=n;i++)
    {
        o[i].x=read();
        o[i].y=read();
        o[i].i=i;//记录原始位置
    }
    sort(o+1,o+1+n,Orz);
    CDQ(1,n);//调用分治
    for(i=1;i<=n;i++)
    {
        write(ans[i]);
        putchar(10);
    }
}
好看的CDQ分治

  时间比较:

  我的CDQ

  

  帆神的树状数组

  

猜你喜欢

转载自www.cnblogs.com/qywyt/p/10393684.html