NYOJ 117 求逆序数 (树状数组 + 离散化思路)

本文介绍了一种计算序列逆序数的有效方法。通过构建值到位置的映射,并利用树状数组来高效统计逆序数,适用于大规模数据处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

求逆序数

时间限制:2000 ms  |  内存限制:65535 KB
难度:5
描述

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。

现在,给你一个N个元素的序列,请你判断出它的逆序数是多少。

比如 1 3 2 的逆序数就是1。

输入
第一行输入一个整数T表示测试数据的组数(1<=T<=5)
每组测试数据的每一行是一个整数N表示数列中共有N个元素(2〈=N〈=1000000)
随后的一行共有N个整数Ai(0<=Ai<1000000000),表示数列中的所有元素。

数据保证在多组测试数据中,多于10万个数的测试数据最多只有一组。
输出
输出该数列的逆序数
样例输入
2
2
1 1
3
1 3 2
样例输出
0
1


思路:要构建一个值到位置的映射,必须要解决值太分散且值的数据太大的问题,可以先将每个数的位置与值用结构体数组存起来,然后按照值的大小排序,然后用1, 2,3,4……代替原先的值,最后将reflect数组按倒序插入树状数组,每插入一个值,就求该值前面的合,即位置在该值前面且大于该值的数,即该值的逆序数。

代码如下:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int tree[1000005];
int reflect[1000005];
int n;
struct node
{
	int val;
	int pos;
}a[1000005];
bool cmp(node n1, node n2)
{
	if(n1.val != n2.val)
	return n1.val < n2.val;
	return n1.pos < n2.pos;
}
void add(int k, int num)
{
	while(k <= n)
	{
		tree[k] += num;
		k += k & -k;
	}
}
long long query(int k)
{
	int sum = 0;
	while(k >= 1)
	{
		sum += tree[k];
		k -= k & -k;
	}
	return sum;
}
int main()
{
	int T, i, j;
	long long sum;
	cin >> T;
	while(T --)
	{
		cin >> n;
		sum = 0;
		memset(tree, 0, sizeof(tree));
		for(i = 1; i <= n; i++)
		{
			scanf("%d", &a[i].val);
			a[i].pos = i;
		}
		sort(a + 1, a + n + 1, cmp);//离散化,将数组按值从小到大排列 
		for(i = 1; i <= n; i++)
		{
			reflect[i] = a[i].pos;//映射数组,从值到位置的映射 
		}
		for(i = n; i >= 1; i--)//将数从大到小添加,每添加一个数,求该数前面有多少个数, 
		{
			add(reflect[i], 1);
			sum += query(reflect[i]) - 1;//-1是除去该数本身 
		}
		cout << sum <<endl;
	}
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值