Luogu P3157 [CQOI2011]动态逆序对

Luogu P3157 [CQOI2011]动态逆序对

解法:树状数组维护主席树
求出原序列的逆序对个数(树状数组预处理)
再依输入把该元素在序列中的逆序对个数减去
但肯定减多了啊,
减多的就是该元素放入被删除元素所组成的序列中的逆序对
加回来(*树状数组+主席树 向左向右分别寻找)
[*我认为的难点,当时zxr讲的时候只听懂了大概思路,感谢lhy的后期帮助TvT]
最后把该元素放入被删除数所组成的序列中
空间开不对RE了好多次q-q

#include<cstdio>
#include<cstring>

int n,m,a[100100],len=0,tree[100100];
struct nod1{int lc,rc;long long c;}tr[20000100];
int root[100100],id[100100],r[100100],l[100100];
long long ans=0;

int lowbit(int x)
{
    return x&-x;
}//玄学lowbit 

void add(int x)
{
    for(int i=x;i<=n;i+=lowbit(i))
        tree[i]+=1;
}
//加入树状数组中 

long long getsum(int x)
{
    int tot=0;
    for(int i=x;i>=1;i-=lowbit(i))
        tot+=tree[i];
    return tot;
}
//树状数组求和 

void update(int &rt,int l,int r,int x)
{
    if(rt==0)
    {
        len++;
        rt=len;
    }
    tr[rt].c++;
    if(l==r)return ;
    int mid=(l+r)/2;
    if(a[x]<=mid)update(tr[rt].lc,l,mid,x);
    else update(tr[rt].rc,mid+1,r,x);
}//加入被删除序列(主席树) 

long long left(int x)
{
    //找左边比我大的数 
    int numx=0,familyx[1010];
    for(int i=x-1;i>=1;i-=lowbit(i))
    {
        numx++;familyx[numx]=root[i];
    }
    long long total=0;
    int l=1,r=n;
    while(l<r)
    {
        int mid=(l+r)/2;
        if(a[x]<=mid)
        {
            for(int i=1;i<=numx;i++)
            {
                //如果小于等于mid 
                total+=tr[tr[familyx[i]].rc].c;
                //那么右边的数都比我大,加起来 
                familyx[i]=tr[familyx[i]].lc;
                //向左边继续寻找 

            }
            r=mid;
        }
        else
        {
            for(int i=1;i<=numx;i++)
            //我大于mid,左边都比我小,暂时没有逆序对 
                familyx[i]=tr[familyx[i]].rc;
                //向右边继续寻找 
            l=mid+1;
        }
    }
    return total;
}

long long right(int x)
{
    //找右边比我小的数 
    int y=n,numx=0,numy=0;
    int familyx[1010],familyy[1010];
    for(int i=x;i>=1;i-=lowbit(i))
    {
        numx++;familyx[numx]=root[i];
    }
    for(int i=y;i>=1;i-=lowbit(i))
    {
        numy++;familyy[numy]=root[i];
    }
    int l=1,r=n,total=0;
    while(l<r)
    {
        int mid=(l+r)/2;
        if(a[x]<=mid)
        {
            //我小于等于mid
            //正常,暂时没有逆序对 、
            //向左边继续寻找 
            for(int i=1;i<=numx;i++)
                familyx[i]=tr[familyx[i]].lc;
            for(int i=1;i<=numy;i++)
                familyy[i]=tr[familyy[i]].lc;
            r=mid;
        }
        else
        {
            //我大于mid,所以lc里都是比我小的 
            //左边都小于我 ,都是逆序对 
            for(int i=1;i<=numx;i++)
            {
                total-=tr[tr[familyx[i]].lc].c;
                //减去1~x比起我小的个数 
                familyx[i]=tr[familyx[i]].rc;
            }
            for(int i=1;i<=numy;i++)
            {
                total+=tr[tr[familyy[i]].lc].c;
                //再加上1~n里比我小的个数
                //就是x~n里比我小的个数 
                familyy[i]=tr[familyy[i]].rc;
            }
            l=mid+1;
        }
    }
    return total;
}

long long work(int x)
{
    int tot=0;
    tot+=left(x);
    //在被删除序列左边查找 
    tot+=right(x);
    //在被删除序列右边查找 
    for(int i=x;i<=n;i+=lowbit(i))
        update(root[i],1,n,x);
    //把这个元素加入被删除序列 
    return tot;
}

int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        id[a[i]]=i;//如果是这个值的元素对应原序列位置 
    }
    for(int i=n;i>=1;i--)
    {//把序列倒着放入树状数组中 
        r[i]=getsum(a[i]-1);
        //getsum得到的计数是在当前元素之前放的,
        //所以在序列中是在当前元素后面的 
        //又因为getsum的是a[i]-1,所以被计数的元素值比当前元素小
        //所以就知道了有多少个序列中在我后面又比我小的 
        ans+=r[i];//把这些加起来就是原序列的逆序对 
        add(a[i]);
    }
    memset(tree,0,sizeof(tree));
    for(int i=1;i<=n;i++)
    {//正着放入
        //!!!但是树状数组中值大的在前 
        l[i]=getsum(n-a[i]);
        //因为值反过来存,所以getsum(n-a[i])
        //就是在我前面(已经放了才能被计数),比我大的 
        add(n-a[i]+1);
        //值大的放前面,小的放后面 
    }
    for(int i=1;i<=m;i++)
    {
        int x;
        printf("%lld\n",ans);
        scanf("%d",&x);
        x=id[x];//用序列中的编号去查找更方便 
        ans-=(l[x]+r[x]);
        //减去在原序列中这个元素的逆序对 
        ans+=work(x);
        //加回减多的逆序对
        //就是这个元素放进被删除数的序列中的逆序对 
    }
}

树状数组求逆序对真的十分神奇!

猜你喜欢

转载自blog.csdn.net/qq_42142540/article/details/80983137