厦门理工学院OJ题解(1147:Problem D)

17 篇文章 2 订阅

Description

__M子__最近在学习线性代数,用的是厦门理工学院自己出的书,第一节课介绍了逆序数的概念。

课本第一页:
定义2 在一个排列中,如果两个数(不一定相邻)的前后位置与标准排列的前后位置不同,即前面的数大于后面的数,则称它们构成一个逆序,一个排列中所有逆序的个数称为这个排列的逆序数。

例如 2431中,21,41,31,43是全部逆序,它的逆序数就是4。

第一次作业布置了很多求逆序数的题目,__M子__这个人很懒,觉得求逆序数每次都手算太麻烦,想编一个程序来解决。

Input

输入数据第一行是一个整数T。下面由T组测试数据组成,对于每一个测试:

第一行包含两个整数n(1 <= n <= 10^5)。第二行包含n个整数A1,A2,…,同比(0 <= Ai <= 10^9)。

Output

对于每一个测试数据,输出一行,每行有一个整数表示逆序数。

Sample Input

2
4
2 4 3 1
4
3 3 3 1

Sample Output

4
3

这题可以用归并排序的思想、线段树、树状数组等方法做。正好硬盘里有线段树模板,就拿来稍加修改交了。
由于数据范围过大,建树前得先对数据进行离散化处理。

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct Node
{
	long long val;
	int pos;

	bool operator<(const Node& temp) const
	{
		return val < temp.val;
	}
};

long long query(vector<long long>& tree, int root, int L, int R, int l, int r);
void insert(vector<long long>& tree, int root, int val, int l, int r);

int main(void)
{
	int t;
	cin >> t;

	while (t--)
	{
		int n;
		cin >> n;
		vector<Node> nums(n);
		for (int i = 0; i < n; ++i)
		{
			cin >> nums[i].val;
			nums[i].pos = i;
		}

		sort(nums.begin(), nums.end());

		int cnt = 0;
		vector<int> arr(n);
		for (int i = 0; i < n; ++i)
			if (i && nums[i].val == nums[i - 1].val)
				arr[nums[i].pos] = cnt - 1;
			else
				arr[nums[i].pos] = cnt++;

		long long ans = 0;
		vector<long long> tree(n * 4, 0);
		for (int i = n - 1; i >= 0; --i)
		{
			ans += query(tree, 0, 0, arr[i] - 1, 0, n - 1);
			insert(tree, 0, arr[i], 0, n - 1);
		}

		cout << ans << endl;
	}
}

long long query(vector<long long>& tree, int root, int L, int R, int l, int r)
{
	if (L == l && R == r)
		return tree[root];
	else if (R < l)
		return 0;
	else
	{
		long long sum = 0;
		int mid = (l + r) >> 1;

		if (L <= mid)
			sum += query(tree, root * 2 + 1, L, min(mid, R), l, mid);
		if (R > mid)
			sum += query(tree, root * 2 + 2, max(mid + 1, L), R, mid + 1, r);

		return sum;
	}
}

void insert(vector<long long>& tree, int root, int val, int l, int r)
{
	if (l != r)
	{
		int mid = (l + r) >> 1;
		if (val <= mid)
			insert(tree, root * 2 + 1, val, l, mid);
		else
			insert(tree, root * 2 + 2, val, mid + 1, r);
	}
	
	++tree[root];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值