HDU 1394 Minimum Inversion Number Segment Tree解法

本题有两个考点:

1 求逆序数的性质

计算逆序数的公式, 一个数arr[i]从前面放到后面,必然会有n-arr[i]-1个数比这个大,那么就有n-arr[i]-1个逆序数增加,同时因为前面少了个arr[i]数,那么就必然有arr[i]个(加上零)数比起小的数失去一个逆序数,总共失去arr[i]个逆序数,所以新的逆序数为增加了n-arr[i]-1-arr[i]个逆序数(当然有可能是减小了,视arr[i]的值而定。

2 如何求一个数列的逆序数

可以使用归并排序来求,也可以使用线段树来求。两者都是二分法的思想,故此时间效率是O(nlgn)

使用线段树来求逆序数是有点难度的,参考了神牛的代码,恍然大悟。

虽然说本题线段树应该不如使用归并排序那么好,但是确实灵活运用线段树的极好例子。

这些数据结构就犹如神兵利器,让我们有可能战胜比自己更加强大的敌人。

#pragma once
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;

class MinimumInversionNumber_2
{
	static const int SIZE = 5001;
	int *segTree;
	inline int lChild(int r) { return r<<1; }
	inline int rChild(int r) { return r<<1|1; }

	void pushUp(int rt)
	{
		segTree[rt] = segTree[lChild(rt)] + segTree[rChild(rt)];
	}

	void build(int l, int r, int rt)
	{
		segTree[rt] = 0;
		if (l == r) return ;

		int m = l + ((r-l)>>1);
		build(l, m, lChild(rt));
		build(m+1, r, rChild(rt));
	}

	void update(int p, int l, int r, int rt)
	{
		if (l == r)
		{
			segTree[rt]++;
			return;
		}
		int m = l + ((r-l)>>1);
		if (p <= m) update(p, l, m, lChild(rt));
		else update(p, m+1, r, rChild(rt));
		pushUp(rt);
	}

	int query(const int L, const int R, int l, int r, int rt)
	{
		if (L <= l && r <= R)
		{
			return segTree[rt];
		}
		int m = l + ((r-l)>>1);
		int res = 0;
		if (L <= m) res += query(L, R, l, m, lChild(rt));
		if (R > m) res += query(L, R, m+1, r, rChild(rt));
		return res;
	}

public:
	MinimumInversionNumber_2() : segTree((int *) malloc(sizeof(int) * (SIZE<<2)))
	{
		int n;
		int arr[SIZE];

		while (~scanf("%d", &n))
		{
			build(0, n-1, 1);
			int sum = 0;
			for (int i = 0; i < n; i++)
			{
				scanf("%d", &arr[i]);
				sum += query(arr[i], n-1, 0, n-1, 1);
				update(arr[i], 0, n-1, 1);
			}

			int ans = sum;
			for (int i = 0; i < n; i++)
			{
				/*计算逆序数的公式, 一个数arr[i]从前面放到后面,必然会有n-arr[i]-1个数比这个大,那么就有n-arr[i]-1个逆序数增加,同时因为前面少了个arr[i]数,那么就必然有arr[i]个(加上零)数比起小的数失去一个逆序数,总共失去arr[i]个逆序数,所以新的逆序数为增加了n-arr[i]-1-arr[i]个逆序数*/
				sum += n - arr[i] - 1 - arr[i];
				ans = min(ans, sum);
			}
			printf("%d\n", ans);
		}
	}

	~MinimumInversionNumber_2()
	{
		free(segTree);
	}
};

HDU 1394 Minimum Inversion Number Segment Tree解法

时间: 2024-10-23 14:04:59

HDU 1394 Minimum Inversion Number Segment Tree解法的相关文章

HDU 1394 Minimum Inversion Number(线段树求最小逆序数对)

HDU 1394 Minimum Inversion Number(线段树求最小逆序数对) ACM 题目地址:HDU 1394 Minimum Inversion Number 题意: 给一个序列由[1,N]构成,可以通过旋转把第一个移动到最后一个. 问旋转后最小的逆序数对. 分析: 注意,序列是由[1,N]构成的,我们模拟下旋转,总的逆序数对会有规律的变化. 求出初始的逆序数对再循环一遍就行了. 至于求逆序数对,我以前用归并排序解过这道题:点这里. 不过由于数据范围是5000,所以完全可以用线

hdu 1394 Minimum Inversion Number(线段树)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 10853    Accepted Submission(s): 6676 Problem Description The inversion number of a given number sequence a1, a2, ..., a

hdu 1394 Minimum Inversion Number 线段树 点更新

// hdu 1394 Minimum Inversion Number 线段树 点更新 // // 典型线段树的单点更新 // // 对于求逆序数,刚开始还真的是很年轻啊,裸的按照冒泡排序 // 求出最初始的逆序数,然后按照公式递推,结果就呵呵了 // // 发现大牛都是用线段树和树状数组之类的做的,而自己又在学 // 线段树,所以就敲了线段树. // // 线段树的节点保存一段区间( L,R )内0,1...n一共出现了多少个. // 因为每个数是0,1,2...n-1且没有重复的数字. /

HDU 1394 Minimum Inversion Number(线段树求逆序对)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1394 解题报告:给出一个序列,求出这个序列的逆序数,然后依次将第一个数移动到最后一位,求在这个过程中,逆序数最小的序列的逆序数是多少? 这题有一个好处是输入的序列保证是0 到 n-1,所以不许要离散化,还有一个好处就是在计算在这个序列中比每个数大和小的数一共有多少个的时候可以在O(1)时间计算出来,一开始我没有意识到,还傻傻的用了两层for循环来每次都计算,当然这样果断TLE了.把一个数从第一个移

HDU 1394 Minimum Inversion Number (数据结构-线段树)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 9514    Accepted Submission(s): 5860 Problem Description The inversion number of a given number sequence a1, a2, ..., an

HDU 1394 Minimum Inversion Number.(线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 ~~~~ 早起一发线段树,开心又快乐.这题暴力也能水过,同时线段树的效率也就体现的尤为明显了,看了大牛的博客,说是还可以用树状数组,点树和合并序列写,现在还不懂,留着以后在写吧. ~~~~ 大致题意:给定一个数字序列,同时由此可以得到n个序列, 要求从n个序列中找到逆序数最小的序列,输出最小逆序数. 首先介绍下逆序数的概念: 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

HDU - 1394 Minimum Inversion Number (线段树求逆序数)

Description The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj. For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seq

hdu 1394 Minimum Inversion Number

题目链接:hdu 1394 Minimum Inversion Number 该题是求最小逆序对的扩展.可以使用树状数组来实现.对于$n$个数的序列$A$,其第$i$个数($i\in [0,n)$)的逆序数$r_i$可以表示为它的角标$i$减去在它之前且不大于它的数的个数.例如对序列A = {1,3,5,9,0,8,5,7,4,2}中的数,A[8] = 4.其逆序数$r_8 = 8 - 3 = 5$,第二个3表示三个在它前面且比它小的数:{1,3,0}.从而我们可以得到第$i$个数的逆序数公式: