BZOJ3295 CQOI2011 动态逆序对 树状数组套线段树
题意:给1到n的一个排列,按照某种顺序依次删除m个元素,每次删除一个元素之前统计整个序列的逆序对数。
题解:
离线倒着做,每次加入一个节点后新增的逆序对数量就是其左边大于它的数的个数(左边数的总数-左边小于它的数的个数)+右边小于它的数的个数
用树状数组维护求和,对于树状数组中每个节点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; }