题目链接:HDU1394
10 1 3 6 9 0 8 5 7 4 2
16
题意:给出从0到n-1这n个数的一个排列,从这个顺序开始,每次都把开头的一个移到最后一个,问这些组合中最逆序数最少是多少。
题目分析:首先我们可以利用线段树求出当前这一组数的逆序数。开始构建线段树的时候令sum数组全为0,每录入一个数x[i]先统计从x[i]到n-1中sum数组的和,然后在sum中对应这个数的位置+1,这样我们每次可以统计出来x[i]是之前多少个数的逆序数,加起来就是整个序列的逆序数。之后需要用一步O(1)的方法统计出其他解,从0到n-1依次移动x[i]到最后一位,由于x[i]已经是第一位,而且这n个数确定是从0到n-1,因此可以肯定有x[i]个数(0到x[i]-1)是原本的逆序数变成顺序的了,那其余的n-1-x[i]肯定是反过来了。因此记上一个序列的逆序数是sum的话,下一个逆序数是sum+n-x[i]-x[i]-1。然后取最小值就好了。
// // main.cpp // HDU1394 // // Created by teddywang on 16/4/29. // Copyright © 2016年 teddywang. All rights reserved. // #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 int sum[200010]; int n; int x[5555]; void pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void update(int x,int l,int r,int rt) { if(l==r) { sum[rt]++; return ; } int m=(l+r)>>1; if(m>=x) update(x,lson); else update(x,rson); pushup(rt); } void build (int l,int r,int rt) { sum[rt]=0; if(l==r) return ; int m=(l+r)>>1; build(lson); build(rson); } 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 ans=0; if(m>=L) ans+=query(L,R,lson); if(m<R) ans+=query(L,R,rson); return ans; } int main() { while(scanf("%d",&n)!=EOF) { 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 ans=sum; for(int i=0;i<n;i++) { sum+=n-x[i]-x[i]-1; ans=min(sum,ans); } printf("%d\n",ans); } }