CDQ分治学习笔记

假装没事ソ 提交于 2020-03-25 03:10:07

CDQ分治是一种神奇的分治算法

它的核心思想大致是这样的

将所有的操作看成一个区间

一个[L,R]的区间分为[L,mid][mid+1,R]这两个子问题

然后处理[L,mid]这个区间对[mid+1,R]这个区间的贡献

这样讲解十分抽象,我们还是来看几个实际问题

二维偏序问题:

 

  给定N个有序对(a,b),求对于每个(a,b),满足a2<a且b2<b的有序对(a2,b2)有多少个

 我们现将所有点排序按一维,这样右区间就不会对左区间产生影响,就可以CDQ了

在具体处理左边区间对右边影响时,

我们将[L,mid]和[mid+1,R]再分别按另一维排序

然后用两个指针扫一下,就好了

 

我们设想一下将二维的偏序加到三维:

  其实做法是差不多的

一维排序,一维CDQ一维树状数组

具体处理时我们将[L,min],[mid+1,R]排序后用两个指针扫时

遇到左边小于右边的情况就将树状数组在a[i].z处加上1

然后在j处的答案就是sum(a[j].z)

上代码(洛谷3810)

# include<iostream>
# include<cstdio>
# include<algorithm>
# include<cmath>
# include<cstring>
using std::sort;
inline int read()
{
    int x=0;
    char ch=getchar();
    while(ch>'9' || ch<'0')
    ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
const int mn = 100005;
struct element{
int x,y,z,s,ans;
};
element a[mn],b[mn];
int tot,ans[mn];
bool cmp1(element m,element n)//按x排序 
{
    if(m.x==n.x && m.y==n.y) return m.z<n.z;
    else if(m.x==n.x) return m.y<n.y;
    else return m.x<n.x;
}
bool cmp2(element m,element n)//按y排序 
{
    if(m.y==n.y) return m.z<n.z;
    return m.y<n.y; 
}
int n,k;
struct BIT{
int siz,tr[mn*2];
void add(int i,int x)
{
    for(i;i<=siz;i+=(i&-i))
       tr[i]+=x;
}
int sum(int i)
{
    int ret=0;
    for(i;i;i-=(i&-i))
      ret+=tr[i];
    return ret;
}
}T;
void CDQ(int l,int r)
{
    if(l==r) return ;
    int mid=l+r>>1;
    CDQ(l,mid);
    CDQ(mid+1,r);
    sort(b+l,b+mid+1,cmp2);
    sort(b+mid+1,b+r+1,cmp2);
    int i=l,j=mid+1;
    while(j<=r)
    {
        while(i<=mid && b[i].y<=b[j].y)
        {
            T.add(b[i].z,b[i].s);
            i++;
        }
        b[j].ans+=T.sum(b[j].z);
        j++;
    }
    for(int s=l;s<i;s++)
     T.add(b[s].z,-b[s].s);
}
int main()
{
    n=read(),k=read();
    T.siz=k;
    for(int i=1;i<=n;i++)
        a[i].x=read(),a[i].y=read(),a[i].z=read();
    sort(a+1,a+1+n,cmp1);
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        cnt++;
        if(a[i].x!=a[i+1].x || a[i].y!=a[i+1].y || a[i].z!=a[i+1].z)
        {
            b[++tot]=a[i];
            b[tot].s=cnt;
            cnt=0;
        }
    }
    CDQ(1,tot);
    /*for(int i=1;i<=tot;i++)
     printf("%d ",b[i].ans);
     printf("\n");*/
    for(int i=1;i<=tot;i++)
        ans[b[i].ans+b[i].s-1]+=b[i].s;
    for(int i=0;i<n;i++)
       printf("%d\n",ans[i]);
    return 0;
}

 

(未完待更)

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!