白书有言:事实上,借助于分治法和Fenwick树,本题可以用更短的代码在更短的运行时间内解决。
当时一直不明白啊,于是就放弃了,反正树套树我也不会(摊手)。
今天突然想到,啊这不就是CDQ分治吗。
整体二分是二分答案,CDQ分治是二分操作。
于是就乱搞一下。
先算出初始序列的逆序对数,顺便算一下每个点的数所成的逆序对数,然后在操作的时候删除。
由于删除的时候会把同一个逆序对删两次,所以还要加上之前重复删掉的,然后用BIT维护一下重复删掉的有多少个就好了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=100000+5; const int M=50000+5; typedef long long ll; struct Query{ int x,p,ti; bool operator<(const Query &rhs)const{ return p<rhs.p; } }q[M],qtmp[M]; int a[N],rank[N],n,m; int c[N],ti[N],now; int del[N],num[N]; ll ans; inline int lowbit(int x){return x&-x;} void add(int x,int flag){ for(;x>0&&x<=n;x+=flag*lowbit(x)){ if(ti[x]!=now)c[x]=0,ti[x]=now; c[x]++; } } int sum(int x,int flag){ int ret=0; for(;x>0&&x<=n;x+=flag*lowbit(x)) if(ti[x]==now)ret+=c[x]; return ret; } void cdq(int l,int r){ if(l==r){ printf("%lld\n",ans); ans-=num[q[l].p]; ans+=del[l]; return; } int mid=l+r>>1,tp1=l,tp2=mid+1; for(int i=l;i<=r;i++) if(q[i].ti<=mid)qtmp[tp1++]=q[i]; else qtmp[tp2++]=q[i]; memcpy(q+l,qtmp+l,sizeof(Query)*(r-l+1)); cdq(l,mid); now++; int j=l; for(int i=mid+1;i<=r;i++){ for(;j<=mid&&q[j].p<q[i].p;j++) add(q[j].x,-1); del[q[i].ti]+=sum(q[i].x,1); } now++; j=mid; for(int i=r;i>mid;i--){ for(;j>=l&&q[j].p>q[i].p;j--) add(q[j].x,1); del[q[i].ti]+=sum(q[i].x,-1); } cdq(mid+1,r); tp1=l,tp2=mid+1; for(int i=l;i<=r;i++) if((q[tp1]<q[tp2]||tp2>r)&&tp1<=mid)qtmp[i]=q[tp1++]; else qtmp[i]=q[tp2++]; memcpy(q+l,qtmp+l,sizeof(Query)*(r-l+1)); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]),rank[a[i]]=i; for(int i=1;i<=n;i++){ num[i]=sum(a[i],1); add(a[i],-1); ans+=num[i]; } now++; for(int i=n;i>=1;i--){ num[i]+=sum(a[i],-1); add(a[i],1); } for(int i=1;i<=m;i++){ scanf("%d",&q[i].x); q[i].p=rank[q[i].x]; q[i].ti=i; } sort(q+1,q+1+m); cdq(1,m); return 0; }