这道题一开始看题,没有什么思路,也是学习线段树以来第一次接触这样的题,后来通过查找资料了解到,这是线段树一个求逆序对的应用,通过学习这方面的知识,总结出可以将它转化为求区间和或者是求区间内1的个数这样一个基本的形式。
这道题我们可以这样想,求逆序对也就是找后面的点比当前点小的这些点的个数的总和,另外我们也可以反过来找前面的点比当前点大的这些点的个数的总和。我用的后面的思路,可能会简单一些吧,每当输入一个值的时候,query一下,找到前面输入的比他大的点的总数,然后将此值赋值为1,updata一下。
1 #include<stdio.h> 2 #include<string.h> 3 #define MAXN 5000+100 4 5 int n, D, sum, tree[MAXN<<2], b[MAXN]; 6 7 void updata(int cur) 8 { 9 tree[D+cur] = 1; 10 for(int i = D+cur; i^1; i >>= 1) 11 tree[i>>1]= tree[i] + tree[i^1]; 12 } 13 14 void query(int cur, int x, int y, int s, int t, int &ans) 15 { 16 int mid = (x+y)>>1, ls = cur << 1, rs = cur << 1 | 1; 17 if(x >= s && y <= t) 18 { 19 ans += tree[cur]; 20 return; 21 } 22 if(mid >= s) 23 query(ls, x, mid, s, t, ans); 24 if(mid + 1 <= t) 25 query(rs, mid+1, y, s, t, ans); 26 } 27 28 void init() 29 { 30 while(~scanf("%d",&n)) 31 { 32 for(D = 1; D < n+2; D <<= 1); 33 memset(tree, 0, sizeof(tree)); 34 sum = 0; 35 for(int i = 0; i < n; i ++) 36 { 37 scanf("%d",&b[i]); 38 if(n > b[i]+1) 39 { 40 int ans = 0; 41 query(1,0,D-1,b[i]+1, n-1,ans); 42 sum +=ans; 43 updata(b[i]); 44 } 45 else updata(b[i]); 46 } 47 int min = sum; 48 for(int i = 0; i < n; i ++) 49 { 50 sum = sum-b[i]+n-b[i]-1; 51 if(min > sum) min = sum; 52 } 53 printf("%d\n",min); 54 } 55 } 56 57 int main() 58 { 59 init(); 60 return 0; 61 }