BZOJ 3295: [Cqoi2011]动态逆序对

10 篇文章 0 订阅
5 篇文章 0 订阅

首先反个顺序,把删除当做从末尾插入。

计算每个数刚被插入的时候原数列对其逆序对的贡献,使用cdq分治,先把[l,r]序列按在原数组中的位置pos排序,然后使用权值BIT维护,就能维护出左半边的数对右半边的数的贡献(正反两遍)。

最后输出答案的时候累加一遍即可

对于初始没被删除的,只需要预先计算一遍他们对所有被删除了的数的逆序对的贡献就可以了,顺便统计出执行完删除操作之后的逆序对数。

//QWsin
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100000+10;
typedef long long ll;

int n,m,num[maxn],pos[maxn];

int C[maxn];
inline void clearbit(){memset(C,0,sizeof C);}
#define lowbit(i) ((i)&-(i))
inline void updata(int pos,int val){
    for(int i=pos;i<=n;i+=lowbit(i)) C[i]+=val; 
}
inline int query(int pos){
    int ret=0;
    for(int i=pos;i;i-=lowbit(i)) ret+=C[i];
    return ret;
}

int vis[maxn],op[maxn];
//vis=1 表示在某个删除操作中 
//op[i] 表示i这个数字最开始对应的OP编号 

struct OP{
    int x,id,ans,t;
    inline void input(int i){
        scanf("%d",&x);id=i;vis[x]=1;ans=0;op[x]=i;
    }
    inline bool operator < (const OP &rhs)const{
        return pos[x]<pos[rhs.x];   
    }
}a[maxn],t[maxn];

ll ans[maxn];

void solve(int l,int r)
{
    if(l==r) return ;

    int mid=(l+r)>>1;
    solve(l,mid);
    solve(mid+1,r);

    for(int i=l;i<=r;++i) t[i]=a[i],t[i].t=(i>mid);

    sort(t+l,t+r+1);

    for(int i=l;i<=r;++i)
        if(t[i].t==0) updata(t[i].x,1);
        else ans[t[i].id]+=query(n)-query(t[i].x);

    for(int i=l;i<=r;++i) if(t[i].t==0) updata(t[i].x,-1);

    for(int i=r;i>=l;--i)
        if(t[i].t==0) updata(t[i].x,1);
        else ans[t[i].id]+=query(t[i].x);

    for(int i=r;i>=l;--i) if(t[i].t==0) updata(t[i].x,-1);
}

int main()
{
    freopen("std.in","r",stdin);
    cin>>n>>m;
    for(int i=1;i<=n;++i) scanf("%d",num+i),pos[num[i]]=i;
    for(int i=1;i<=m;++i) a[m+1-i].input(m+1-i);

    ll sum=0;

    for(int i=1;i<=n;++i) 
        if(!vis[num[i]]) 
        {
            sum+=query(n)-query(num[i]);
            updata(num[i],1);
        }
        else {
            int t=query(n)-query(num[i]);
            ans[a[op[num[i]]].id]+=t;
        }
    clearbit();

    for(int i=n;i>=1;--i)
        if(!vis[num[i]]) 
        {
//          sum+=query(num[i]);//一开始你还把原序列的逆序对算成了二倍(扶额)
            updata(num[i],1);
        }
        else {
            int t=query(num[i]);
            ans[a[op[num[i]]].id]+=t;
        }

    clearbit();
    solve(1,m);

    ans[0]=sum;
    for(int i=1;i<=m;++i) ans[i]+=ans[i-1];
    for(int i=m;i>=1;--i) printf("%lld\n",ans[i]);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值