P3157 [CQOI2011]动态逆序对
设 $val[i]$ 为位置 $i$ 的值
维护 $ansL[i]$ 表示位置 $i$ 的数左边所有大于 $val[i]$ 的数的数量
维护 $ansR[i]$ 表示位置 $i$ 的数右边所有小于 $val[i]$ 的数的数量
考虑先求出一开始总的逆序对数 $ans$
每次删除一个数 (位置为 $p$ ) 就把 $ans$ 减去 $ansL[p]+ansR[p]$
但是这样会多减,因为有些数在更之前就删掉了
考虑维护这些删掉的数的贡献,发现那么我们的 $ans$ 多扣了以后还要加上之前删除的位置小于 $p$ 且大于 $val[p]$ 的数以及位置大于 $p$ 且小于 $val[p]$ 的数
因为每次删掉相当于单点修改,查询就是区间查询,所以考虑用树状数组套动态开点权值线段树来维护
具体看代码
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e5+7,M=3e7+7; int n,m; int T[N];//这个树状数组维护ansL,ansR和ans inline void T_add(int x,int y) { while(x<=n) T[x]+=y,x+=x&-x; } inline int T_sum(int x) { int res=0; while(x) res+=T[x],x-=x&-x; return res; } int rt[N],S[M],L[M],R[M],cnt;//树状数组套动态开点的权值线段树 int pos,ql,qr,res,pd; inline void S_ins(int &o,int l,int r)//往线段树插入一个数 { if(!o) o=++cnt; S[o]++; if(l==r) return; int mid=l+r>>1; if(pos<=mid) S_ins(L[o],l,mid); else S_ins(R[o],mid+1,r); } inline void S_query(int o,int l,int r)//在线段树中查询一个区间的数的数量 { if(l>qr||r<ql||!o) return; if(l>=ql&&r<=qr) { res+=S[o]*pd; return; }//pd判断是+还是- int mid=l+r>>1; S_query(L[o],l,mid); S_query(R[o],mid+1,r); } inline int Query(int pl,int pr,int vl,int vr)//查询当前位置>=pl,<=pr,权值>=vl,<=vr的数的数量 { res=0; ql=vl,qr=vr; pd=1; for(int i=pr;i;i-=i&-i) S_query(rt[i],1,n); pd=-1; for(int i=pl-1;i;i-=i&-i) S_query(rt[i],1,n);//注意pl-1因为是要减去的不包括pl return res; } int ansL[N],ansR[N]; ll ans; int val[N],id[N]; int main() { n=read(),m=read(); for(int i=1;i<=n;i++) { val[i]=read(),id[val[i]]=i; ansL[i]=(i-1)-T_sum(val[i]); ans+=ansL[i]; T_add(val[i],1); } for(int i=1;i<=n;i++) T[i]=0; for(int i=n;i;i--) ansR[i]=T_sum(val[i]-1),T_add(val[i],1); int a,p; while(m--) { printf("%lld\n",ans); a=read(); p=id[a]; ans-=ansL[p]+ansR[p]-Query(1,p,a+1,n)-Query(p+1,n,1,a-1);//动态维护ans pos=a; while(p<=n) S_ins(rt[p],1,n),p+=p&-p; } return 0; }