题解:洛谷 P1908 逆序对
【题目来源】
【题目描述】
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 \(a_i>a_j\) 且 \(i<j\) 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
【输入】
第一行,一个数 \(n\),表示序列中有 \(n\)个数。
第二行 \(n\) 个数,表示给定的序列。序列中每个数字不超过 \(10^9\)。
【输出】
输出序列中逆序对的数目。
【输入样例】
6
5 4 2 6 3 1
【输出样例】
11
【算法标签】
《洛谷 P1908 逆序对》 #树状数组# #递归# #离散化# #排序#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 使用长整型
const int MAX_N = 500005; // 定义数组最大长度
int n; // 数组长度
int a[MAX_N], b[MAX_N]; // a: 原始数组, b: 临时数组
int res; // 逆序对总数
// 归并排序并统计逆序对
void merge(int l, int r)
{
if (l >= r) return; // 递归终止条件:区间长度为1
int mid = (l + r) / 2; // 计算中点
merge(l, mid); // 递归处理左半部分
merge(mid + 1, r); // 递归处理右半部分
// 合并两个有序区间并统计逆序对
int i = l, j = mid + 1, k = l;
while (i <= mid && j <= r)
{
if (a[i] <= a[j]) // 左半部分元素小于等于右半部分
b[k++] = a[i++];
else // 左半部分元素大于右半部分
{
b[k++] = a[j++];
res += mid - i + 1; // 统计逆序对数量
}
}
// 处理剩余元素
while (i <= mid) b[k++] = a[i++];
while (j <= r) b[k++] = a[j++];
// 将排序好的数据复制回原数组
for (int i = l; i <= r; i++)
a[i] = b[i];
}
signed main()
{
// 加速输入输出
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
// 输入数组长度和元素
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
// 调用归并排序计算逆序对
merge(1, n);
// 输出逆序对总数
cout << res;
return 0;
}
// 使用acwing的模板二刷
#include <bits/stdc++.h>
using namespace std;
#define int long long // 使用长整型
const int N = 500005; // 定义数组最大长度
int n; // 数组长度
int q[N], tmp[N]; // q: 原始数组, tmp: 临时数组
// 归并排序并统计逆序对数量
int merge_sort(int l, int r)
{
if (l >= r) return 0; // 递归终止条件:区间长度为1
int mid = l + r >> 1; // 计算中点
int res = merge_sort(l, mid) + merge_sort(mid + 1, r); // 递归处理左右子区间
// 合并两个有序区间并统计逆序对
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
{
if (q[i] <= q[j]) // 左半部分元素小于等于右半部分
tmp[k++] = q[i++];
else // 左半部分元素大于右半部分
{
tmp[k++] = q[j++];
res += mid - i + 1; // 统计逆序对数量
}
}
// 处理剩余元素
while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];
// 将排序好的数据复制回原数组
for (int i = l, j = 0; i <= r; i++, j++)
q[i] = tmp[j];
return res; // 返回当前区间的逆序对总数
}
signed main()
{
// 输入数组长度和元素
cin >> n;
for (int i = 1; i <= n; i++)
cin >> q[i];
// 调用归并排序并输出逆序对总数
cout << merge_sort(1, n) << endl;
return 0;
}
【运行结果】
6
5 4 2 6 3 1
11
浙公网安备 33010602011771号