大意:n次操作原串查询逆序数,求出所有串中最小的逆序数。
求逆序数属于线段树的统计问题,建立空树,每次进行插点时进行一次query操作即可。n次操作可以套用结论:如果是0到n的排列,那么如果把第一个数放到最后,对于这个数列,逆序数是减少a[i],而增加n-1-a[i]。
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 5 const int maxn = 5e3 + 10; 6 int sumv[maxn << 2]; 7 8 void PushUp (int rt) { 9 sumv[rt] = sumv[rt * 2] + sumv[rt * 2 +1]; 10 } 11 12 void build (int l, int r, int rt) { 13 sumv[rt] = 0; 14 if (l == r) { 15 return ; 16 } 17 int m = (l + r) / 2; 18 build (l, m, rt * 2); 19 build (m + 1, r, rt * 2 + 1); 20 } 21 22 void update (int p, int l, int r, int rt) { 23 if (l == r) { 24 sumv[rt] ++; 25 return ; 26 } 27 int m = (l + r) / 2; 28 if (p <= m) { 29 update (p, l, m, rt * 2); 30 } 31 if (p > m) { 32 update (p, m + 1, r, rt * 2 + 1); 33 } 34 PushUp (rt); 35 } 36 37 int query (int L, int R, int l, int r, int rt) { 38 if (L <= l && r <= R) { 39 return sumv[rt]; 40 } 41 int m = (l + r) / 2; 42 int ret = 0; 43 if (L <= m) { 44 ret += query (L, R, l, m, rt * 2); 45 } 46 if (R > m) { 47 ret += query (L, R, m + 1, r, rt * 2 + 1); 48 } 49 return ret; 50 } 51 52 int x[maxn]; 53 54 int main () { 55 int n; 56 while (~scanf ("%d", &n)) { 57 build (0, n - 1, 1); 58 int sum = 0; 59 for (int i = 0 ; i < n; ++ i) { 60 scanf ("%d", &x[i]); 61 sum += query (x[i], n - 1, 0, n - 1, 1); 62 update (x[i], 0, n - 1, 1); 63 } 64 int res = sum; 65 for (int i = 0; i < n; ++ i) { 66 sum = sum + n - x[i] - 1 - x[i]; 67 res = min (res, sum); 68 } 69 printf ("%d ", res); 70 } 71 return 0; 72 }