【经典题目】逆序对的数量 离散化技巧

P1908 逆序对

逆序对

题目描述

逆序对就是序列中 \(a_i>a_j\) 且 \(i<j\) 的有序对。

以下解法时间复杂度均为 \(O(log n)\)

两种算法比较:

  • 归并分治的做法属于离线,并且修改了原始数组;他不受数据值域的限制
  • 树状数组的做法属于在线,每次都可以实时的拿到答案,并且不需要修改原始数组,但是受数据值域的限制

归并分治解法

C++ 复制代码
#include <bits/stdc++.h>
using namespace std;

int help[500005];

long long merge(vector<int>& arr, int l, int m, int r) {
	long long ans = 0;
	for (int i = m, j = r; i >= l; i--) {
		while (j > m && arr[i] <= arr[j]) {
			j--;
		}
		ans += j - m;
	}
	int i = l, a = l, b = m+1;
	while (a <= m && b <= r) {
		help[i++] = arr[a] < arr[b] ? arr[a++] : arr[b++];
	}
	while (a <= m) {
		help[i++] = arr[a++];
	}
	while (b <= r) {
		help[i++] = arr[b++];
	}
	for (int i = l; i <= r; i++) {
		arr[i] = help[i];
	}
}

long long f(vector<int>& arr, int l, int r) {
	if (l == r) {
		return 0;
	}
	int mid = (r - l) / 2 + l;
	return f(arr, l, m) + f(arr, m+1, r) + merge(arr, l, m, r);
}

int main() {
	
	return 0;
}

树状数组解法

首先,逆序对就是每遍历到一个数,看看在他后面的数中有几个比他大,因此可以从右往左遍历,统计 \([1, x-1]\) 词频的词频,每次遍历到新的数字需要单点修改,与范围查询前缀和,因此选择树状数组,注意到数据范围过大,如果不做处理将会达到 \(10^9\) 的量级。可以确定的是,我们不需要具体数字大小,只需要相对大小,因此可以离散化处理。

具体处理如下,将原始数据从小到大排序,它的下标就为新值,再将该值刷回数组中,这样将值域从 \(10^9\) 减小到了和数组大小一致的量级,且保证了数据的相对大小

处理原始数据,保证新值不小于 1

C++ 复制代码
void transfer() {
    sort(arr+1, arr+n+1);
    len = 1;
    for (int i = 2; i <= n; i++) {
        if (arr[len] != arr[i]) arr[++len] = arr[i];
    }
    for (int i = 1; i <= n; i++) {
        sorted[i] = lower_bound(arr+1, arr+1+len, sorted[i]) - arr;
    }
}

总代码

c++ 复制代码
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 5e5+7;
int n;
int arr[MAXN];
int sorted[MAXN];
int tree[MAXN];
int len;
void transfer() {
    sort(arr+1, arr+n+1);
    len = 1;
    for (int i = 2; i <= n; i++) {
        if (arr[len] != arr[i]) arr[++len] = arr[i];
    }
    for (int i = 1; i <= n; i++) {
        sorted[i] = lower_bound(arr+1, arr+1+len, sorted[i]) - arr;
    }
}

void add(int i, int v) {
    while (i <= n) {
        tree[i] += v;
        i += i & (-i);
    }
}

long long sum(int i) {
    long long ans = 0;
    while (i > 0) {
        ans += tree[i];
        i -= i & (-i);
    }
    return ans;
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> arr[i];
        sorted[i] = arr[i];
    }
    transfer();
    long long ans = 0;
    for (int i = n; i > 0; i--) {
        int x = sorted[i];
        ans += sum(x-1);
        add(x, 1);
    }
    cout << ans << "\n";
    
    return 0;
}
相关推荐
何其有幸.6 小时前
实验3-3 比较大小(PTA|C语言)
c语言·数据结构·算法
东阳马生架构7 小时前
Sentinel源码—8.限流算法和设计模式总结二
算法·设计模式·sentinel
老饼讲解-BP神经网络7 小时前
一篇入门之-评分卡变量分箱(卡方分箱、决策树分箱、KS分箱等)实操例子
算法·决策树·机器学习
何其有幸.8 小时前
实验6-3 使用函数求特殊a串数列和(PTA|C语言)
c语言·数据结构·算法
不会计算机的捞地8 小时前
【数据结构入门训练DAY-24】美国大选
数据结构·算法
明月看潮生8 小时前
青少年编程与数学 02-018 C++数据结构与算法 11课题、分治
c++·算法·青少年编程·编程与数学
Echo``9 小时前
2:QT联合HALCON编程—图像显示放大缩小
开发语言·c++·图像处理·qt·算法
.似水9 小时前
2025.4.22_C_可变参数列表
java·c语言·算法
Felven9 小时前
A. Ideal Generator
java·数据结构·算法
MoonBit月兔9 小时前
双周报Vol.70: 运算符重载语义变化、String API 改动、IDE Markdown 格式支持优化...多项更新升级!
ide·算法·哈希算法