题目描述
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 ai>aj 且 i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
Update:数据已加强。
输入格式
第一行,一个数 n,表示序列中有 n个数。
第二行 n 个数,表示给定的序列。序列中每个数字不超过 10^9。
输出格式
输出序列中逆序对的数目。
输入输出样例
输入 #1复制
6 5 4 2 6 3 1
输出 #1复制
11
思路
看到题目的第一反应就是纯暴力,两个for循环,从第一个开始,去遍历后序的所有元素,如果比它更大那么计数+1,时间复杂度为O(n!),题目的给出的数据最多有50万个,如果用这种暴力法直接GG
那么就要想怎么去提高时间效率,对于很长的数组我们没法直接看出有多少个逆序对,但是如果数组只有两个呢,这时思路就会清晰一点,我们去考虑采取二分思想(算贪心吗?)去把数组拆分,去找子数组的逆序对,再合并,再重复....
总结一下思路,本题求逆序对的步骤:
- 把数组拆分成两部分,左子数组,右子数组,直到拆分到只剩一个元素
- 开始就开始两两合并,在合并的时候比较大小,如果左边的大于右边的说明这是逆序对
- 不断重复第二步,直到数组又全部合并一起,排序正常(不存在逆序对)
换句话说就是原数组逆序对 = 子数组内的逆序对 + 排序后右数组对左数组的逆序对
看到这大家应该很自然能想到这些步骤不就是归并排序的步骤吗,确实是这样,这个题目就可以利用归并排序来解决,只要添加个cnt变量,在每次比较时如果a[i]>a[j],cnt++
AC CODE
#include <bits/stdc++.h>
using namespace std;
#define maxn 500005
typedef long long ll;
ll n, a[maxn], tmp[maxn];
ll cnt;
inline int read()
{ //快读
char ch = getchar();
int x = 0, f = 1;
while (ch < '0' || ch > '9')
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
x = x * 10 + (ch ^ 48), ch = getchar();
return x * f;
}
void merge(ll arr[], ll temp[], ll left, ll center, ll right)
{
ll i = left, j = center + 1;
for (ll k = left; k <= right; k++)
{
if (i > center)
temp[k] = arr[j++];
else if (j > right)
temp[k] = arr[i++];
else if (arr[i] <= arr[j])
temp[k] = arr[i++];
else
{
temp[k] = arr[j++];
cnt += center - i + 1;
}
}
for (ll k = left; k <= right; k++)
arr[k] = temp[k];
}
void mergesort(ll arr[], ll temp[], ll left, ll right)
{
if (left < right)
{
ll mid = (left + right) / 2;
mergesort(arr, temp, left, mid);
mergesort(arr, temp, mid + 1, right);
merge(arr, temp, left, mid, right);
}
}
int main()
{
n = read();
for (int i = 0; i < n; i++)
a[i] = read();
mergesort(a, tmp, 0, n - 1);
cout << cnt;
return 0;
}