地址:http://acm.hdu.edu.cn/showproblem.php?pid=1394
题意:给n个数字(0到n-1无重复),可以拿前面任意m个放到末尾。求拿多少个数字放到末尾后数列的逆序数最小。
mark:非常经典的一个题目,来自zoj月赛。先用线段树/点树/树状数组/合并排序求出原数列的逆序数,然后递推出所有情况的逆序数取最小。
这题真的是非常经典,所以4种方法我都写了一次。
代码:
线段树(62ms、284k、991B):
1 # include <stdio.h> 2 # include <string.h> 3 4 5 # define m ((l+r)>>1) 6 # define lson l,m,p<<1 7 # define rson m+1,r,p<<1|1 8 9 10 int val[5010 << 2] ; 11 int a[5010] ; 12 13 14 int query (int L, int R, int l, int r, int p) 15 { 16 if (L == l && R == r) return val[p] ; 17 if (R <= m) return query(L,R,lson) ; 18 if (L > m) return query (L,R,rson) ; 19 return query(L,m,lson) + query(m+1,R,rson) ; 20 } 21 22 23 void update (int a, int l, int r, int p) 24 { 25 val[p]++ ; 26 if (l == r){ 27 val[p] = 1 ; 28 return ; 29 } 30 if (a <= m) update(a,lson) ; 31 else if (a > m) update (a,rson) ; 32 } 33 34 35 int main () 36 { 37 int num, sum, ans, n, i ; 38 while (~scanf ("%d", &n)) 39 { 40 memset (val, 0, sizeof(val)) ; 41 sum = 0 ; 42 for (i = 0 ; i < n ; i++) 43 { 44 scanf ("%d", &a[i]) ; 45 sum += query(a[i]+1, n, 1, n, 1) ; 46 update(a[i]+1, 1, n, 1) ; 47 } 48 ans = sum ; 49 for (i = 0 ; i < n - 1 ; i++) 50 { 51 sum = sum + n-1 - 2*a[i] ; 52 if (ans > sum) ans = sum ; 53 } 54 printf ("%d\n", ans) ; 55 } 56 return 0 ; 57 }