离线倒着做,每次加入一个节点后新增的逆序对数量就是其左边大于它的数的个数(左边数的总数-左边小于它的数的个数)+右边小于它的数的个数
用树状数组维护求和,对于树状数组中每个节点v所对应的区间线段树维护区间[l,r]中大于v的数的个数。
最后唯一的问题就是指针版线段树MLE……
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; #define lowbit(x) (x&(-x)) #define ll long long const int MAXN=100000+2; const int MAXM=50000+2; typedef struct NODE{ int l,r; ll c; NODE *lchild,*rchild; NODE(){} NODE(int _l,int _r):l(_l),r(_r),c(0),lchild(0),rchild(0){} } *TREE; TREE root[MAXN]; int N,M,a[MAXN],p[MAXN],q[MAXM]; ll bit[MAXN],ans[MAXM]; bool flag[MAXN]; void Update(int x){ while(x<=N) bit[x]++,x+=lowbit(x); } ll Summation(int x){ ll ret=0; while(x) ret+=bit[x],x-=lowbit(x); return ret; } void Pushup(TREE &x){ x->c=0; if(x->lchild) x->c+=x->lchild->c; if(x->rchild) x->c+=x->rchild->c; } void Add(TREE &x,int l,int r,int p){ if(!x) x=new NODE(l,r); if(l==r){ x->c++; return; } int m=(l+r)>>1; if(p<=m) Add(x->lchild,l,m,p); else Add(x->rchild,m+1,r,p); Pushup(x); } void Insert(int p,int v){ v++; while(v<=N) Add(root[v],1,N,p),v+=lowbit(v); } ll Calc(TREE &x,int l,int r){ if(!x) return 0; if(x->l>=l && x->r<=r) return x->c; int m=(x->l+x->r)>>1; ll ret=0; if(l<=m) ret+=Calc(x->lchild,l,r); if(r>m) ret+=Calc(x->rchild,l,r); return ret; } ll Query(int l,int r,int v){ ll ret=0; while(v) ret+=Calc(root[v],l,r),v-=lowbit(v); return ret; } int main(){ scanf("%d %d",&N,&M); for(int i=1;i<=N;i++){ scanf("%d",a+i); p[a[i]]=i; } for(int i=1;i<=M;i++){ scanf("%d",q+i); q[i]=p[q[i]],flag[q[i]]=1; } for(int i=1;i<=N;i++) if(!flag[i]){ Update(a[i]),ans[M]+=Summation(N)-Summation(a[i]); Insert(i,a[i]); } memset(bit,0,sizeof(bit)); for(int i=1;i<=N;i++) if(!flag[i]) Update(i); for(int i=M;i;i--){ Insert(q[i],a[q[i]]); Update(q[i]); ans[i-1]=ans[i]+Summation(q[i]-1)-Query(1,q[i]-1,a[q[i]])+Query(q[i]+1,N,a[q[i]]); } for(int i=0;i<M;i++) printf("%lld\n",ans[i]); return 0; }