题意:
给出一个由0到n-1组成的序列,每次把队首的元素移到队尾,求形成的n个序列中逆序对数目的最小值(n<=5000)
题解:
首先可以对初始数组求一次逆序对(O(nlogn))
很自然的想到,如果每次变幻数组后再求逆序对(O(n?logn)),显然会爆
但我们可以做到O(1)的递推
假设原始数组的逆序对个数是sum个,如果把x[1]放到x[n]后面,逆序列个数会减少x[1]个,相应会增加n-(x[1]+1)个
即有
sum+=(n-(x[i]+1))-(x[i]);
意识到这个就很好做了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define getmid int m=(l+r)>>1
const int maxn=5555;
int sum[maxn<<2];
void Pushup(int rt)
{sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void Build(int l,int r,int rt)
{sum[rt]=0;if(l==r) return;getmid;Build(lson);Build(rson);
}
void Update(int p,int l,int r,int rt)
{if(l==r){sum[rt]++;return;}getmid;if(p<=m) Update(p,lson);else Update(p,rson);Pushup(rt);
}
int Query(int L,int R,int l,int r,int rt)
{if(L<=l&&R>=r) return sum[rt];getmid;int ret=0;if(L<=m) ret+=Query(L,R,lson);if(R>m) ret+=Query(L,R,rson);return ret;
}int x[maxn];int main()
{int n;while(~scanf("%d",&n)){Build(0,n-1,1);int sum=0;for(int i=0;i<n;i++){scanf("%d",&x[i]);sum+=Query(x[i],n-1,0,n-1,1);Update(x[i],0,n-1,1);}int ret=sum;for(int i=0;i<n;i++){sum+=n-x[i]-1-x[i];ret=min(ret,sum);}printf("%d\n",ret);}return 0;
}