http://acm.hdu.edu.cn/showproblem.php?pid=1394
刚看的时候感觉和线段树并没有关系啊,但是看别人的解题报告却都用了很多的方法,还有用的就是线段树,树状数组,暴力也过了,还有归并排序也能通过,感觉就是条条道路通罗马啊,但是我却想不出来一个可以用的方法。也可能是我把以前学的数学知识给忘的差不多了的原因吧,回去还要好好补数学呢。
想解决这个题首先得知道什么是逆序数,这是数学中概念,经过查找,我回想起来了逆序数就是一列数中出现逆序的个数。左移序列之后得到新的序列的逆序数的个数就等与当前总的个数减左边的数的逆序数a[i],然后加上新增的逆序数n-a[i]-1;这样这个题的思路就有了。
暴力求解就是求每个数的逆序数,然后加到一起。之后再左移序列。
//暴力解法
#include <stdio.h> #include <string.h> int main() { int tmp[5001], b[5001]; int n, i, j; while(scanf("%d", &n)!=EOF){ for(i = 0; i < n; i ++){ scanf("%d", &tmp[i]); } __int64 t = 0; memset(b, 0, sizeof(b)); for(i = 0; i < n; i ++){ for(j = i+1; j < n; j ++){ if(tmp[i] > tmp[j]) b[i] ++; } t += b[i]; } __int64 min = t; for(i = 0; i < n; i ++){ // printf("t = %I64d\n", t); t = t-tmp[i]+n-1-tmp[i]; if(min > t)min = t; } // printf("t = %d\n", t); printf("%I64d\n", min); } return 0; }使用线段树解此题时,每次都要查找在该数出现之前已经出现的大于该数的个数,该个数就是该数的逆序数,然后累加求出全部的逆序数,最后进行求解。
//线段树解法 #include <stdio.h> #define MAX 5005 typedef struct _node { int left; int right; int value; }node; node no[MAX]; int ans; void initTree(int left, int right, int i) { no[i].left = left, no[i].right = right; no[i].value = 0; if(left == right)return; int mid = (left+right)/2; initTree(left, mid, i*2); initTree(mid+1, right, i*2+1); } //update void modify(int index, int i) { if(no[i].left == no[i].right){ no[i].value = 1; return; } int mid = (no[i].left + no[i].right)>>1; if(index <= mid){ modify(index, i*2); } else { modify(index, i*2+1); } no[i].value = no[i*2].value + no[i*2+1].value; } //query void query(int left, int right, int i) { if(no[i].left == left && no[i].right == right){ ans += no[i].value; return ; } int mid = (no[i].left + no[i].right)/2; if(right <= mid){ query(left, right, i*2); } else if(left > mid){ query(left, right, i*2+1); } else { query(left, mid, i*2); query(mid + 1, right, i*2+1); } } int main() { int n, i, a[MAX]; while(scanf("%d", &n)!=EOF){ initTree(0, n-1, 1); ans = 0; for(i = 0; i < n; i ++){ scanf("%d", a+i); query(a[i], n-1, 1); modify(a[i], 1); } int min = ans; for(i = 0; i < n; i ++){ ans = ans - a[i] + n - a[i] - 1; if(min > ans)min = ans; } printf("%d\n", min); } return 0; }