首先反个顺序,把删除当做从末尾插入。
计算每个数刚被插入的时候原数列对其逆序对的贡献,使用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;}新闻热点
疑难解答