bzoj3295 动态逆序对

时间:2024-09-21 15:05:56

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。

Output

输出包含m行,依次为删除每个元素之前,逆序对的个数。

将原数列建立归并树,归并树每个节点记录区间排序后结果、被删除的数的个数、树状数组记录前i个数被删了几个,每删除一个数前查找左边比它大的数和右边比它小的数的个数并更新答案。

#include<cstdio>
#include<algorithm>
inline int read(){
int x=,c=getchar();
while(c>||c<)c=getchar();
while(c>&&c<)x=x*+c-,c=getchar();
return x;
}
inline void mins(int&a,int b){if(a>b)a=b;}
inline void maxs(int&a,int b){if(a<b)a=b;}
long long ans=;
int t[],v[],id[];
int vs[][],bit[][],dt[][];
int X,v0;
void build(int L,int R,int h=){
int*x=vs[h];
if(L<R){
int M=L+R>>;
build(L,M,h+);
build(M+,R,h+);
int p1=L,p2=M+,*p3=x+L,*y=vs[h+];
while(p1<=M&&p2<=R)*(p3++)=y[(y[p1]<y[p2]?p1:p2)++];
while(p1<=M)*(p3++)=y[p1++];
while(p2<=R)*(p3++)=y[p2++];
}else x[L]=v[L];
}
void s1(int L,int R,int h=){
int*x=vs[h];
if(R<X){
int p=std::upper_bound(x+L,x+R+,v0)-(x+L);
ans-=R+-L-p;
for(int*a=bit[h]+L-;p;p-=p&-p)ans-=a[p];
ans+=dt[h][L];
return;
}
if(L!=R){
int M=L+R>>;
s1(L,M,h+);
if(X->M)s1(M+,R,h+);
}
}
void s2(int L,int R,int h=){
int*x=vs[h];
if(X<L){
int p=std::lower_bound(x+L,x+R+,v0)-(x+L);
ans-=p;
for(int*a=bit[h]+L-;p;p-=p&-p)ans+=a[p];
return;
}
if(L!=R){
int M=L+R>>;
if(X<M)s2(L,M,h+);
s2(M+,R,h+);
}
}
void del(int L,int R,int h=){
int*x=vs[h];
++dt[h][L];
int p=std::lower_bound(x+L,x+R+,v0)-(x+L)+;
for(int*a=bit[h]+L-,N=R-L+;p<=N;p+=p&-p)++a[p];
if(L!=R){
int M=L+R>>;
if(X<=M)del(L,M,h+);
else del(M+,R,h+);
}
}
int n,m;
int main(){
n=read();m=read();
for(int i=,a;i<=n;i++){
v[i]=a=read();
id[a]=i;
ans+=i-;
for(int w=a;w;w-=w&-w)ans-=t[w];
while(a<=n)++t[a],a+=a&-a;
}
build(,n);
for(int i=;i<=m;i++){
printf("%lld\n",ans);
v0=read();
X=id[v0];
s1(,n);
s2(,n);
del(,n);
}
return ;
}