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