题意:给一个初始的数组,有m个操作,每次操作从数组中删除值为x的数,每次删除之前求数组的逆序数。
思路:先建n颗线段树,第 i 颗线段树保存数组前 i 个数的信息,然后再用树状数组记录这个初始数组并求出初始的逆序数ans,假设删除了x,这个时候逆序数时多少呢,只要求出x贡献了多少逆序数对t,然后用ans-t即可,先找到x的下标cur,在cur之前找到有t1个数比 x 大,然后在cur之后找到t2个数比 x 小,t1+t2就是 x 贡献的逆序数 t,怎么求呢,可用主席树求出第 i 颗线段树比 x 大的数个数即可,然后删除一个数,用树状数组套主席树更新即可。写的是洛谷ac的代码,在uva中,maxn改为1e6,同时会有多组数据。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long LL;
const int maxn=5e5+10;
int sum[maxn*20],ls[maxn*20],rs[maxn*20];
int rt[maxn],c[maxn],last[maxn];
int n,cnt=0;
int low(int x)
{
return x&(-x);
}
int Sum(int x)
{
int res=0;
while(x)
{
res+=c[x];
x-=low(x);
}
return res;
}
void add(int x,int v)
{
while(x<=n)
{
c[x]+=v;
x+=low(x);
}
}
void update(int &o,int l,int r,int k,int v)
{
if(!o)o=++cnt;
sum[o]+=v;
if(l==r)return;
int m=(l+r)/2;
if(k<=m)update(ls[o],l,m,k,v);
else update(rs[o],m+1,r,k,v);
}
void modify(int i,int x,int v)
{
while(i<=n)
{
update(rt[i],1,n,x,v);
i+=low(i);
}
}
int allsum,pmin;
void query(int o,int l,int r,int k)
{
if(l==r||!sum[o])return;
int m=(l+r)/2,res=0;
if(k>m)
{
pmin+=sum[ls[o]];
if(sum[rs[o]])
query(rs[o],m+1,r,k);
}
else query(ls[o],l,m,k);
}
void get(int i,int x)
{
while(i)
{
allsum+=sum[rt[i]];
query(rt[i],1,n,x);
i-=low(i);
}
}
int main()
{
int x,m;
LL ans=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
last[x]=i;
ans+=i-1-Sum(x);
add(x,1);
modify(i,x,1);
}
while(m--)
{
scanf("%d",&x);
printf("%lld\n",ans);
add(x,-1);
modify(last[x],x,-1);
allsum=pmin=0;// 前last个数中总共有几个数,以及前last个数中有几个比x小
get(last[x],x);
int allmin=Sum(x);//所有数中比x小的个数
ans=ans-(allsum-pmin)-(allmin-pmin);
}
}