[BZOJ3295] [Cqoi2011]动态逆序对(带修改主席树)
题目描述
对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
输入输出格式
输入格式:
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
输出格式:
输出包含m行,依次为删除每个元素之前,逆序对的个数。
输入输出样例
5 4 1 5 3 4 2 5 1 4 2
5 2 2 1 样例解释 (1,5,3,4,2),(1,3,4,2),(3,4,2),(3,2),(3)。
说明
N<=100000 M<=50000
题解
原来还以为自己已经会带修改主席树了呢……才发现自己还是太naive……
然后找到的题解全是CDQ分治的……我这个蒟蒻有点方……
然后发现还是zcysky大佬写的最吼啦
还是来详细的说一说
先考虑无修改的逆序对怎么做?
很明显,用树状数组(虽然我今天之前一直以为逆序对个数只能用归并做)
我们记$a1[i]$表示在$i$之前且比$i$大的数的个数(注意,这里的i指的是位置),那么很明显答案为$\sum _{i=1}^n a[i]$
代码实现
1 for(int i=1;i<=n;++i){ 2 val[i]=read(),pos[val[i]]=i; 3 a1[i]=ask(n)-ask(val[i]); 4 ans+=a1[i]; 5 for(int j=val[i];j<=n;j+=lowbit(j)) ++c[j]; 6 }
记$a2[i]$表示在$i$之后且比$i$小的数的个数,只要把上面那个倒着推就行了
1 for(int i=n;i;--i){ 2 a2[i]=ask(val[i]-1); 3 for(int j=val[i];j<=n;j+=lowbit(j)) ++c[j]; 4 }
接下来我们考虑修改操作。
每一次将一个数删除,减少的逆序对个数是多少?
很明显是$a1[i]+a2[i]$,然后我们就可以做啦
于是评测机表示并不想理你并丢给你一堆WA
这个时候我们发现自己忽略了一个关键的问题,如果$a1[i]$和$a2[i]$中表示的数已经有被删除了的怎么办?
我们只要把这些被删除的数减去即可
具体来说,我们可以考虑用一个带修改主席树维护
因为主席树维护的是前缀和
如果按照一般思想,一个一个去更改太浪费时间了
我们想到,前缀和可以用树状数组的思想来维护
于是我们可以用树状数组的思想建主席树
于是每一次更改就可以减少到做$log n$次了
所以每一次删去一个数,我们就在主席树上插入这个数
要算答案时,只要减去$a1[i]$和$a2[i]$,再把删除的数加回来就好了
只要在主席树上$[1,i-1]$区间中大于$val[i]$的数的个数和$[i+1,n]$区间中小于$val[i]$的数的个数即可
1 //minamoto 2 #include<bits/stdc++.h> 3 #define N 100005 4 #define M 5000005 5 #define ll long long 6 using namespace std; 7 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 8 char buf[1<<21],*p1=buf,*p2=buf; 9 inline ll read(){ 10 #define num ch-'0' 11 char ch;bool flag=0;ll res; 12 while(!isdigit(ch=getc())) 13 (ch=='-')&&(flag=true); 14 for(res=num;isdigit(ch=getc());res=res*10+num); 15 (flag)&&(res=-res); 16 #undef num 17 return res; 18 } 19 char obuf[1<<24],*o=obuf; 20 void print(ll x){ 21 if(x>9) print(x/10); 22 *o++=x%10+48; 23 } 24 int L[M],R[M],sum[M],rt[N]; 25 int val[N],pos[N],xx[N],yy[N],c[N],a1[N],a2[N]; 26 int n,cnt,q;ll ans=0; 27 inline int lowbit(int x){return x&(-x);} 28 int ask(int x){ 29 int s=0; 30 for(int i=x;i;i-=lowbit(i)) s+=c[i]; 31 return s; 32 } 33 void update(int &now,int l,int r,int k){ 34 if(!now) now=++cnt; 35 ++sum[now]; 36 if(l==r) return; 37 int mid=(l+r)>>1; 38 if(k<=mid) update(L[now],l,mid,k); 39 else update(R[now],mid+1,r,k); 40 } 41 int querysub(int x,int y,int v){ 42 int cntx=0,cnty=0,ans=0;--x; 43 for(int i=x;i;i-=lowbit(i)) xx[++cntx]=rt[i]; 44 for(int i=y;i;i-=lowbit(i)) yy[++cnty]=rt[i]; 45 int l=1,r=n; 46 while(l<r){ 47 int mid=(l+r)>>1; 48 if(v<=mid){ 49 for(int i=1;i<=cntx;++i) ans-=sum[R[xx[i]]]; 50 for(int i=1;i<=cnty;++i) ans+=sum[R[yy[i]]]; 51 for(int i=1;i<=cntx;++i) xx[i]=L[xx[i]]; 52 for(int i=1;i<=cnty;++i) yy[i]=L[yy[i]]; 53 r=mid; 54 } 55 else{ 56 for(int i=1;i<=cntx;++i) xx[i]=R[xx[i]]; 57 for(int i=1;i<=cnty;++i) yy[i]=R[yy[i]]; 58 l=mid+1; 59 } 60 } 61 return ans; 62 } 63 int querypre(int x,int y,int v){ 64 int cntx=0,cnty=0,ans=0;--x; 65 for(int i=x;i;i-=lowbit(i)) xx[++cntx]=rt[i]; 66 for(int i=y;i;i-=lowbit(i)) yy[++cnty]=rt[i]; 67 int l=1,r=n; 68 while(l<r){ 69 int mid=(l+r)>>1; 70 if(v>mid){ 71 for(int i=1;i<=cntx;++i) ans-=sum[L[xx[i]]]; 72 for(int i=1;i<=cnty;++i) ans+=sum[L[yy[i]]]; 73 for(int i=1;i<=cntx;++i) xx[i]=R[xx[i]]; 74 for(int i=1;i<=cnty;++i) yy[i]=R[yy[i]]; 75 l=mid+1; 76 } 77 else{ 78 for(int i=1;i<=cntx;++i) xx[i]=L[xx[i]]; 79 for(int i=1;i<=cnty;++i) yy[i]=L[yy[i]]; 80 r=mid; 81 } 82 } 83 return ans; 84 } 85 int main(){ 86 //freopen("testdata.in","r",stdin); 87 n=read(),q=read(); 88 for(int i=1;i<=n;++i){ 89 val[i]=read(),pos[val[i]]=i; 90 a1[i]=ask(n)-ask(val[i]); 91 ans+=a1[i]; 92 for(int j=val[i];j<=n;j+=lowbit(j)) ++c[j]; 93 } 94 memset(c,0,sizeof(c)); 95 for(int i=n;i;--i){ 96 a2[i]=ask(val[i]-1); 97 for(int j=val[i];j<=n;j+=lowbit(j)) ++c[j]; 98 } 99 while(q--){ 100 print(ans),*o++='\n'; 101 int x=read();x=pos[x]; 102 ans-=(a1[x]+a2[x]-querysub(1,x-1,val[x])-querypre(x+1,n,val[x])); 103 for(int j=x;j<=n;j+=lowbit(j)) update(rt[j],1,n,val[x]); 104 } 105 fwrite(obuf,o-obuf,1,stdout); 106 return 0; 107 }