BZOJ 3295 动态逆序对

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删

除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数

Input

输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。

以下n行每行包含一个1到n之间的正整数,即初始排列。

以下m行每行一个正整数,依次为每次删除的元素。

N<=100000 M<=50000

Output

输出包含m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4 1 5 3 4 2 5 1 4 2

Sample Output

5 2 2 1

样例解释 (1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

思路: 可以倒过来想,我每次向一个序列中加入一个数,求得他前边的数和他产生的逆序对的个数和他后边的数和他产生的逆序对的个数。  那么我可以设一个时间戳,没有删除的元素 为0 ,那么最后删除的我可以设为1 ,

然后这样就有了一个时间序,那么我们把每个数的位置记录为 x 价值为自身 y  那么没插入一个数,实际上求逆序对就是在求

t1<t2  && x1<x2 && y1>y2  和 t1<t2 && x1> x2 && y1<y2 的个数,这样的话题目就可以转变为求三维偏序了,那么cdq分治一下。对于y 维护个树状数组就可以了.

代码: 

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N =1e5+5;

struct node
{
    int t;
    int x,y;
    int f;
}a[N],tmp[N];

ll c[N];
ll cc[N];
int aa[N];
int pre[N];
int post[N];
int n,m;
int pos[N];

bool cmp(node a,node b)
{
    return a.x<b.x;
}

int lowbit(int x)
{
    return x&(-x);
}

void update1(int x,int val)
{
    for(;x>0; x-=lowbit(x)) c[x]+=val;
}

int query1(int x)
{
    int sum=0;
    for(;x<=n;x+=lowbit(x)) sum+=c[x];
    return sum;
}

void update2(int x,int val)
{
    for(;x<=n;x+=lowbit(x)) c[x]+=val;
}

int query2(int x)
{
    int sum=0;
    for(;x>0;x-=lowbit(x)) sum+=c[x];
    return sum;
}

void cdq(int l,int r)
{
    //cout<<"l "<<l<<" r "<<r<<endl;
    if(l>=r) return ;
    int mid=(l+r)>>1;
    cdq(l,mid);
    cdq(mid+1,r);
    int sz=r-l+1;
    for(int i=l;i<=mid;i++) a[i].f=0;
    for(int i=mid+1;i<=r;i++) a[i].f=1;
    int tot=0;
    for(int i=l;i<=r;i++) tmp[++tot]=a[i];
    /*
    cout<<"l "<<l<<" r "<<r<<endl;
    for(int i=1;i<=tot;i++){
        cout<<tmp[i].t<<" "<<tmp[i].x<<" "<<tmp[i].y<<endl;
    }
    */

    sort(tmp+1,tmp+tot+1,cmp);
    for(int i=1;i<=tot;i++){
        if(tmp[i].f==0){
            update1(tmp[i].y,1);
        }
        else{
            pre[tmp[i].t]+=query1(tmp[i].y); /// 扫描前边比他大的个数
            //cout<<"time "<<tmp[i].t<<" ni "<<pre[tmp[i].t ]<<endl;
        }
    }

    for(int i=1;i<=tot;i++) if(tmp[i].f==0) update1(tmp[i].y,-1);


    for(int i=tot;i>=1;i--){
        if(tmp[i].f==0){
            update2(tmp[i].y,1);
        }
        else{
            post[tmp[i].t]+=query2(tmp[i].y);  /// 扫描后边比他小的个数
        }
    }
    for(int i=1;i<=tot;i++) if(tmp[i].f==0) update2(tmp[i].y,-1);
    for(int i=l;i<=r;i++){
        a[i]=tmp[i-l+1];
    }
}

bool cmp1(node a,node b)
{
    return a.t<b.t;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i].y);
        a[i].x=i;
        pos[a[i].y]=i;
        a[i].t=0;
    }
    int tim=n;
    int val;
    for(int i=1;i<=m;i++){
        scanf("%d",&val);
        a[pos[val]].t=tim--;
    }

    sort(a+1,a+n+1,cmp1);
    /*
    for(int i=1;i<=n;i++){
        cout<<a[i].t<<" "<<a[i].x<<" "<<a[i].y<<endl;
    }
    */
    cdq(1,n);

    ll ni=0;
    for(int i=0;i<=n;i++){
        ni+=1ll*pre[i];
        ni+=1ll*post[i];
    }
    ll now=0;
    for(int i=n;i>=n-m+1;i--){
        printf("%lld\n",ni);
        ni-=1ll*(pre[i]+post[i]);
        //cout<<pre[i]<<" "<<post[i]<<endl;
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/yjt9299/article/details/81910828