[AtCoder Beginner Contest 396] F - Rotated Inversions
题目描述
题目链接
解法一、树状数组求逆序对
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int n, m;
LL tr[N]; //tr[i]表示当前数组元素中值 <= i的有多少
int lowbit(int x) {
return x & -x;
}
void add(int x, int c) {
for(int i = x; i <= m; i += lowbit(i)) {
tr[i] += c;
}
}
LL sum(int x) {
LL res = 0;
for(int i = x; i > 0; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
int main() {
cin >> n >> m;
vector<int> a(n);
for (int i = 0; i < n; i ++) {
cin >> a[i];
}
//存储每个值在原数组中的位置
vector<vector<int>> g(m);
for (int i = 0; i < n; i ++) {
g[a[i]].pb(i);
}
LL ans = 0;
//利用树状数组进行数组初始时的逆序数(即k = 0时)
//把值x存储到位置x + 1
//那么对于当前位置为i的值a[i],其位置在之前并比其值大的数组元素有sum(m) - sum(a[i] + 1)
//然后在把当前的值的个数增加存入到树状数组中, 即add(a[i] + 1, 1)
for(auto x : a) { // x = a[i]
ans += sum(m) - sum(x + 1);
add(x + 1, 1);
}
cout << ans << '\n';
for (int c = 1; c < m; ++c) {
LL c1 = 0, c2 = 0;
for (int idx = 0; idx < g[m - c].size(); idx ++) {
//如果x + c = m,在这一轮中x的值将会变为(x + c) % m = 0,逆序数就可能会发生变化
//分别讨论增加与减少的数量,其值分别为c1、c2
//在某个变为0的数之前有多少不为0的数呢?值为g[m - c][idx] - idx(减去了跟它同组变为0且在它前面的数)
c1 += g[m - c][idx] - idx;
//同理需要计算,某个变为0的数之后有多少不为0的数。值为(n - 1) - g[m - c][idx] - (g[m - c].size() - 1 - idx)
c2 += n - 1 - g[m - c][idx] - (g[m - c].size() - 1 - idx);
}
ans += c1 - c2;
cout << ans << '\n';
}
return 0;
}
解法二、归并排序求逆序对
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int n, m;
LL tr[N]; //tr[i]表示当前数组元素中值 <= i的有多少
int lowbit(int x) {
return x & -x;
}
void add(int x, int c) {
for(int i = x; i <= m; i += lowbit(i)) {
tr[i] += c;
}
}
LL sum(int x) {
LL res = 0;
for(int i = x; i > 0; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
//求解数组arr的逆序数
LL merge(vector<int>& arr, int left, int right) {
if (left >= right) {
return 0;
}
int mid = (left + right) >> 1;
LL res = merge(arr, left, mid) + merge(arr, mid + 1, right);
vector<int> temp(right - left + 1);
int i = left, j = mid + 1, k = 0;
while(i <= mid && j <= right) {
if(arr[i] <= arr[j]) {
temp[k ++] = arr[i ++];
} else {
res += mid - i + 1;
temp[k ++] = arr[j ++];
}
}
while(i <= mid) temp[k ++] = arr[i ++];
while(j <= right) temp[k ++] = arr[j ++];
for (int p = 0; p < k; p ++) {
arr[left + p] = temp[p];
}
return res;
}
int main() {
cin >> n >> m;
vector<int> a(n);
for (int i = 0; i < n; i ++) {
cin >> a[i];
}
//存储每个值在原数组中的位置
vector<vector<int>> g(m);
for (int i = 0; i < n; i ++) {
g[a[i]].pb(i);
}
LL ans = merge(a, 0, n - 1);
cout << ans << '\n';
for (int c = 1; c < m; ++c) {
LL c1 = 0, c2 = 0;
for (int idx = 0; idx < g[m - c].size(); idx ++) {
//如果x + c = m,在这一轮中x的值将会变为(x + c) % m = 0,逆序数就可能会发生变化
//分别讨论增加与减少的数量,其值分别为c1、c2
//在某个变为0的数之前有多少不为0的数呢?值为g[m - c][idx] - idx(减去了跟它同组变为0且在它前面的数)
c1 += g[m - c][idx] - idx;
//同理需要计算,某个变为0的数之后有多少不为0的数。值为(n - 1) - g[m - c][idx] - (g[m - c].size() - 1 - idx)
c2 += n - 1 - g[m - c][idx] - (g[m - c].size() - 1 - idx);
}
ans += c1 - c2;
cout << ans << '\n';
}
return 0;
}