http://acm.hdu.edu.cn/showproblem.php?pid=1394
部分来自http://blog.csdn.net/libin56842/article/details/8531117
写给那些 刚入门线段树,开始和我一样对解题迷茫的人.
逆序数的概念
如:0 3 4 1 2
设逆序数初始n = 0;
由于0后面没有比它小的,n = 0
3后面有1,2 n = 2
4后面有1,2,n = 2+2 = 4;
所以该序列逆序数为 4
其根据题意移动产生的序列有
3 4 1 2 0 逆序数:8
4 1 2 0 3 逆序数:6
1 2 0 3 4 逆序数:2
2 0 3 4 1 逆序数:4
所以最小逆序数为2
首先题目规定 输入的n个数是0到n-1的,不会大于等于n。假设一个序列 a[n],,逆序总数是 m , 第一个数是x, 那么x的后面有 x 个数比 x 小,因为包含0, 则第一个数移动最后一个,则 x 的 前面 有 n - x - 1个比 x 大, 所以这时候 逆序总数就是 m - x + n - x - 1;以此类推。 所以,我们只用知道初始序列的逆序数是多少即可, 当然方法很多,这里讲一下线段树。如何建树,每输入一个数a[i],就查询 比这个数大的区间[a[i]+1, n]内,有多少个数 前面 已经输入过了, 就是存在,那么用线段树便可以解决了。
#include <stdio.h> #include <iostream> #include <string.h> using namespace std; #define manx 5005 struct Tree { int left; int right; int num; //该区间内,已经出现的个数 }tree[4 * manx]; int a[manx]; void BuildTree(int i, int l, int r); void Update(int i, int id); int Query(int i, int l, int r); int main() { int n; while(~scanf("%d", &n)) { int ans = 0; BuildTree(1, 1, n); for(int i = 0; i < n; i++) { scanf("%d", &a[i]); Update(1, a[i] + 1); ans += Query(1, a[i] + 2, n);//询问比他大区间已经出现的次数 //printf("ans = %d\n", ans); } int m = ans; for(int i = 0; i < n; i++) { m = m + n - 2 * a[i] - 1; if(m < ans) ans = m; } printf("%d\n", ans); } return 0; } void BuildTree(int i, int l, int r)//建树 { tree[i].left = l; tree[i].right = r; tree[i].num = 0;//初始化全部为0,因为还没有输入 if(tree[i].left == tree[i].right) return; int mid = (l + r) >> 1; BuildTree(i << 1, l, mid); BuildTree(i << 1 | 1, mid + 1, r); } void Update(int i, int id)//更新 { if(tree[i].left == id && tree[i].right == id) { tree[i].num = 1;//输入过了,标记为1 return; } int mid = (tree[i].left + tree[i].right) >> 1; if(id > mid) Update(i << 1 | 1, id); else Update(i << 1, id); tree[i].num = tree[i << 1].num + tree[i << 1 | 1].num;//更新此区间内,已经出现过的总数 } int Query(int i, int l, int r)//询问该区间已经出现的次数 { //printf("%d %d %d\n", i, l, r); if(l > r) return 0; if(tree[i].left == l && tree[i].right == r) return tree[i].num; int mid = (tree[i].left + tree[i].right) >> 1; if(r <= mid) return Query(i << 1, l, r); else if(l > mid) return Query(i << 1 | 1, l, r); else return Query(i << 1, l, mid) + Query(i << 1 | 1, mid + 1, r); } //这里是mid+1