HDU 1394 Minimum Inversion NumberMinimum Inversion Number(线段树)

http://acm.hdu.edu.cn/showproblem.php?pid=1394

部分来自http://blog.csdn.net/libin56842/article/details/8531117

写给那些 刚入门线段树,开始和我一样对解题迷茫的人.

题意 求最小逆序数

逆序数的概念

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。如2 4 3 1中,21,43,41,31是逆序,逆序数是4,为偶排列。

题目要求序列做如下0 到 n-1变换后, 求出逆序数分别是多少, 输出 最小的逆序数

如: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;以此枚举每一个 i 移到最后的情况。 所以,我们只用知道初始序列的逆序数是多少即可, 当然方法很多,这里讲一下线段树。如何建树,每输入一个数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
", 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
", 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
", 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

  

 

原文地址:https://www.cnblogs.com/tenlee/p/4420107.html