题意:一个由0..n-1组成的序列,每次可以把队首的元素移到队尾,求形成的序列中的最小逆序数(逆序数定义可以参考百度百科)
思路:假设有长为10的一个序列,由0-9组成,那么,这个序列的第一位数n的逆序对的个数一定是n个!!!因为,在这个序列后面,比n小的个数一定只有n个。假设已知给出的序列的逆序数位ans,序列位t。如果把t[1]放到t[N]后面,逆序列个数会减少t[1]个,相应会增加N-(t[1]+1)个,么,只需知道原序列的逆序数,就可以通过递推,求出变化后的最小逆序数了
(转载胡浩dalao的博客!!!)
#include <cstdio> #include <algorithm> using namespace std; #define lson l , m , rt << 1 #define rson m + 1 , r , rt << 1 | 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 ; int m = (l + r) >> 1; build(lson); build(rson); } void update(int p,int l,int r,int rt) {//节点维护[l,r]区间的点的个数 if (l == r) { sum[rt] ++; return ; } int m = (l + r) >> 1; 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]; } int m = (l + r) >> 1; 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 ans = 0; for (int i = 0 ; i < n ; i ++) {//求出当前序列的逆序数 scanf("%d",&x[i]); ans += query(x[i] , n - 1 , 0 , n - 1 , 1);//查找从x[i]到n-1的数的个数,即是当前的逆序数 update(x[i] , 0 , n - 1 , 1); } int ret = ans; for (int i = 0 ; i < n ; i ++) {//递推求出变化的最小逆序数 ans += n - x[i] - x[i] - 1; ret = min(ret , ans); } printf("%d ",ret); } return 0; }