题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394
题目大意:
给定一个0到n-1的数字组成的序列,它的逆序数,然后把第一个数字放到末尾,得到一个新的序列,再求逆序数,再把新序列的第一个数字放到末尾,一直这样做,求所有这些序列的逆序数的最小值。
题目思路:
可以先求出起初的序列的逆序数。然后根据逆序数的定义,把一个数字从开头移动到末尾,逆序数的改变量是什么?求出这个改变量,然后剩下的所有序列的逆序数就都求出来了。
这样考虑:一个数字 b[i] 在开头,比它大的数字有 b[i] 个,也就是说和这个数字组成了 b[i] 个逆序,把它放到最后,这个数字可以组成 n-1-b[i] 个逆序,所以逆序数的增量是 n - 1 - b[i] - b[i] ,这样就可以根据原来的序列的逆序数求出剩下的所有序列的逆序数了~
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 using namespace std; 6 const int MAX = 5000+10; 7 #define lson l, m, rt<<1 8 #define rson m+1, r, rt<<1|1 9 int a[MAX<<2], b[MAX], n; 10 void pushup(int rt) { 11 a[rt] = a[rt<<1] + a[rt<<1|1]; 12 } 13 void build(int l, int r, int rt) { 14 if (l == r) {a[rt] = 0; return;} 15 int m = (l + r) >> 1; build(lson); build(rson); pushup(rt); 16 } 17 void update(int p, int l, int r, int rt) { 18 if (l == r) {a[rt]++; return;} 19 int m = (l + r) >> 1; 20 if (p <= m) update(p, lson); else update(p, rson); 21 pushup(rt); 22 } 23 int query(int L, int R,int l, int r, int rt) { 24 if (L <= l && R >= r) {return a[rt];} 25 int m = (l + r) >> 1, ret = 0; 26 if (L <= m) ret += query(L, R, lson); 27 if (R > m) ret += query(L, R, rson); 28 return ret; 29 } 30 void init() { 31 while (~scanf("%d", &n)) { 32 int i, sum = 0, ans; 33 build(0, n - 1, 1); 34 for (i = 0; i < n; ++i) { 35 scanf("%d", b+i); 36 sum += query(b[i]+1, n-1, 0, n-1, 1); 37 update(b[i], 0, n-1, 1); 38 } 39 ans = sum; 40 for (i = 0; i < n; ++i) { 41 sum += (n-1-2*b[i]); 42 if (sum < ans) ans = sum; 43 } 44 printf("%d\n", ans); 45 } 46 } 47 int main(void) { 48 init(); 49 return 0; 50 }
这题是线段树的单点更新