wyx528命题
题解:树状数组+线段树
首先利用权值树状数组求出以i个点开头的逆序对数。
选出一个位置i,将i-n中所有小于等于p[i]的位置选出来重新排好序后放回,可以发现这样做对于位于i前面的点的逆序对数没有影响,因为原先在后面现在还在后面,对于i-n中大于p[i]的点的逆序对数也没有影响因为无论怎么移动,选出的位置一定小于他。那么有影响的其实就是选出了的点,他们的逆序对数变为了0。
用线段树维护区间最小值所在的位置(如果两个位置的值一样,那么优先选择后面的位置),每次将一个位置的逆序对数清零后,将这个位置的数值付成极大值,这样就可以保证每个点至多修改一次,然后从i-n的区间中一直修改,知道a[i]=inf位置,输出此时序列中的总逆序对数。时间复杂度应该在o(nlogn)左右。
刚开始还想到了树套数的写法,用区间线段树套权值线段树,貌似也可以,但是时间复杂度不如这种方法优越。
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define N 500003 #define LL long long #define inf 1000000000 using namespace std; int n,m; int a[N],b[N],p[N],cnt; LL f[N],tr[N*4],ans; int tree[N*4]; int cmp(int x,int y) { return b[x]<b[y]; } int lowbit(int x) { return x&(-x); } void change(int x,LL v) { for (int i=x;i<=cnt;i+=lowbit(i)) tr[i]+=v; } LL sum(int x) { LL ans=0; for (int i=x;i;i-=lowbit(i)) ans+=tr[i]; return ans; } void update(int x) { if (a[tree[x<<1|1]]<=a[tree[x<<1]]) tree[x]=tree[x<<1|1]; else tree[x]=tree[x<<1]; } void build(int now,int l,int r) { if (l==r) { tree[now]=l; return; } int mid=(l+r)/2; build(now<<1,l,mid); build(now<<1|1,mid+1,r); update(now); } int pd(int x,int y) { if (a[y]<=a[x]) return y; else return x; } int query(int now,int l,int r,int ll,int rr) { if (l>=ll&&r<=rr) return tree[now]; int mid=(l+r)/2; int ans=0; if (ll<=mid) ans=query(now<<1,l,mid,ll,rr); if (rr>mid) { int t=query(now<<1|1,mid+1,r,ll,rr); if (ans==0) ans=t; else ans=pd(ans,t); } return ans; } void pointchange(int now,int l,int r,int x) { if (l==r) { a[l]=inf; f[l]=0; return ; } int mid=(l+r)/2; if (x<=mid) pointchange(now<<1,l,mid,x); else pointchange(now<<1|1,mid+1,r,x); update(now); } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&b[i]),p[i]=i; sort(p+1,p+n+1,cmp); cnt=0; for (int i=1;i<=n;i++) if (b[p[i]]!=b[p[i-1]]) a[p[i]]=++cnt; else a[p[i]]=cnt; for (int i=n;i>=1;i--) { change(a[i],1); f[i]=sum(a[i]-1); ans+=f[i]; } printf("%lld\n",ans); build(1,1,n); for (int i=1;i<=m;i++) { int x; scanf("%d",&x); while (a[x]!=inf) { int t=query(1,1,n,x,n); ans-=f[t]; pointchange(1,1,n,t); } printf("%lld\n",ans); } }