来自FallDream的博客,未经允许,请勿转载,谢谢。
对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
n<=100000 m<=50000
呃我感觉我中毒了 看到这道题第一个想法就是对询问分块
建出主席树,然后把操作压到栈里面,然后每次查询都遍历这个栈,计算答案。然后当操作达到一定数量的时候重建主席树。
块的大小取大约$sqrt{nlogn}$比较优秀 复杂度$O(m(k+logn)+frac{n}{k}nlogn)$
当然更靠谱的做法是cdq分治,这道题其实就是一道三维偏序 先对时间排序,然后在分治的时候对x排序,用树状数组/线段树处理y维即可。
复杂度$O(nlog^{2}n)$
询问分块
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #define mp(x,y) make_pair(x,y) #define MN 100000 #define ll long long using namespace std; 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 * 10 + ch - '0';ch = getchar();} return x * f; } ll ans=0,Ans[MN+5]; int n,m,sz,rt[MN+5],cnt=0,s[MN+5],qx[MN+5],pos[MN+5],q[MN+5],top=0; bool b[MN+5]; struct Tree{int l,r,x;}T[MN*20]; void ins(int x,int nx,int k) { int l=1,r=n,mid;T[nx].x=T[x].x+1; while(l<r) { mid=l+r>>1; if(k<=mid) T[nx].r=T[x].r,T[nx].l=++cnt,r=mid,x=T[x].l,nx=T[nx].l; else T[nx].l=T[x].l,T[nx].r=++cnt,l=mid+1,x=T[x].r,nx=T[nx].r; T[nx].x=T[x].x+1; } } int query(int x,int nx,int k) { int l=1,r=n,mid,sum=0; while(l<r) { mid=l+r>>1; if(k<=mid) x=T[x].l,nx=T[nx].l,r=mid; else sum+=(T[T[nx].l].x-T[T[x].l].x),x=T[x].r,nx=T[nx].r,l=mid+1; } return sum+T[nx].x-T[x].x; } void build() { cnt=0; for(int i=1;i<=n;i++) b[i]?rt[i]=rt[i-1]:(ins(rt[i-1],rt[i]=++cnt,s[i]),0); } int main() { n=read();m=read();sz=sqrt(n*15); for(int i=1;i<=n;i++) pos[s[i]=read()]=i; for(int i=1;i<=m;i++) q[i]=read(),b[pos[q[i]]]=1; build(); for(int i=2;i<=n;i++) if(!b[i]) ans+=T[rt[i-1]].x-query(0,rt[i-1],s[i]); for(int i=m;i;i--) { if(pos[q[i]]!=1) ans+=T[rt[pos[q[i]]-1]].x-query(0,rt[pos[q[i]]-1],q[i]); if(pos[q[i]]!=n) ans+=query(rt[pos[q[i]]],rt[n],q[i]); for(int j=1;j<=top;j++) if((qx[j]>q[i]&&pos[qx[j]]<pos[q[i]])||(qx[j]<q[i]&&pos[qx[j]]>pos[q[i]])) ++ans; Ans[i]=ans;b[pos[q[i]]]=0; qx[++top]=q[i]; if(top>=sz) build(),top=0; } for(int i=1;i<=m;i++) printf("%lld ",Ans[i]); return 0; }
cdq分治
#include<iostream> #include<cstdio> #include<algorithm> #define ll long long #define MN 100000 #define N 131072 using namespace std; 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 * 10 + ch - '0';ch = getchar();} return x * f; } int n,m,T[N*2+5],ans=0,s[MN+5];ll Ans[MN+5],pos[MN+5]; struct ques{int kind,t,x,y;}q[MN+5],b[MN+5]; int query(int l,int r) { int sum=0; for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1) { if(~l&1) sum+=T[l+1]; if( r&1) sum+=T[r-1]; } return sum; } void renew(int x,int ad) { T[x+=N]+=ad; for(x>>=1;x;x>>=1)T[x]=T[x<<1]+T[x<<1|1]; } bool cmpX(ques x,ques y){return x.x<y.x;} void solve(int l,int r) { if(l>=r) return; int mid=l+r>>1; for(int i=l;i<=r;i++) b[i]=q[i],b[i].kind=(i<=mid)?1:2; sort(b+l,b+r+1,cmpX); for(int i=l;i<=r;i++) if(b[i].kind==1) renew(b[i].y,1); else Ans[b[i].t]+=query(b[i].y,n); for(int i=l;i<=r;i++) if(b[i].kind==1)renew(b[i].y,-1); for(int i=r;i>=l;i--) if(b[i].kind==1)renew(b[i].y,1); else Ans[b[i].t]+=query(1,b[i].y); for(int i=l;i<=r;i++) if(b[i].kind==1)renew(b[i].y,-1); solve(l,mid); solve(mid+1,r); } bool cmpT(ques x,ques y){return x.t<y.t;} int main() { n=read();m=read();int tot=n; for(int i=1;i<=n;i++)pos[s[i]=read()]=i; for(int i=1;i<=m;i++)q[pos[read()]].t=tot--; for(int i=1;i<=n;q[i].x=i,q[i].y=s[i],++i)if(!q[i].t) q[i].t=tot--; sort(q+1,q+n+1,cmpT); solve(1,n);ll sum=0; for(int i=1;i<=n;i++) sum+=Ans[i]; for(int i=n;i>n-m;i--) printf("%lld ",sum),sum-=Ans[i]; return 0; }