从CF1676H2看逆序对的两种求法

Problem - 1676H2 - Codeforces

1|0思路

原问题可以以直接转化成求 ai>=aj(i<j) 的数量。

2|0归并排序

原理很直接,归并排序就是为了减少逆序对的数量,所以直接在操作的时候记录减少的数量即可

排序后的数组无逆序对,归并排序的合并操作中,每次后段首元素被作为当前最小值取出时,前段剩余元素个数之和即是合并操作减少的逆序对数量

但是实现比较麻烦

std::vector<int> a, temp; int n; i64 merge(int left, int mid, int right) { int i(left), j(mid), k(left); i64 count(0); while(i <= mid - 1 and j <= right) { if (a[i] < a[j]) { temp[k++] = a[i++]; } else {//因为题目要求a[i] >= a[j] temp[k++] = a[j++]; count += mid - i;//又因为合并操作已保证左区间有序,所以i之后到mid的a[i]都大于等于a[j] } } while(i <= mid - 1) temp[k++] = a[i++]; while(j <= right) temp[k++] = a[j++]; for (i = left; i <= right; i++) a[i] = temp[i]; return count; } i64 mergeSort(int left, int right) { int mid(0); i64 count(0); if (right > left) { mid = left + right >> 1; count = mergeSort(left, mid); count += mergeSort(mid + 1, right); count += merge(left, mid + 1, right); } return count; } i64 aInversion(int n) { temp.resize(n); return mergeSort(0, n - 1); } void solve() { std::cin >> n; a.resize(n); for (auto& x : a) std::cin >> x; std::cout << aInversion(n) << '\n'; }

3|0树状数组or线段树

题目说了ain

考虑用 ai 来作为一段 01 前缀和中 1 的下标,即通过前缀和来计数

从后往前扫,每次先查询包括该下标到 1​ (因为要求 ai>=aj​)的前缀和,显然就是对于当前数字的逆序对个数

  • 树状数组
template <typename T> struct Fenwick { int n; std::vector<T> a; Fenwick(int n_ = 0) { init(n_); } void init(int n_) { n = n_; a.assign(n, T{}); } void add(int x, const T &v) { for (int i = x + 1; i <= n; i += i & -i) { a[i - 1] = a[i - 1] + v; } } T sum(int x) { T ans{}; for (int i = x; i > 0; i -= i & -i) { ans = ans + a[i - 1]; } return ans; } }; void solve() { int n; std::cin >> n; Fenwick<int> fen(n); std::vector<int> a(n); for (auto& x : a) std::cin >> x, x--; i64 ans(0); for (int i = n - 1; i >= 0; i--) { ans += fen.sum(a[i] + 1);//这里是因为前面对数据做了--防越界处理,而该fenwick的实现中查询是右开的 fen.add(a[i], 1); } std::cout << ans << '\n'; }
  • 线段树
template<class Info> struct SegmentTree { int n; std::vector<Info> info; SegmentTree() : n(0) {} SegmentTree(int n_, Info v_ = Info()) { init(n_, v_); } template<class T> SegmentTree(std::vector<T> init_) { init(init_); } void init(int n_, Info v_ = Info()) { init(std::vector(n_, v_)); } template<class T> void init(std::vector<T> init_) { n = init_.size(); info.assign(4 << std::__lg(n), Info()); std::function<void(int, int, int)> build = [&](int p, int l, int r) { if (r - l == 1) { info[p] = init_[l]; return; } int m = (l + r) / 2; build(2 * p, l, m); build(2 * p + 1, m, r); pull(p); }; build(1, 0, n); } void pull(int p) { info[p] = info[2 * p] + info[2 * p + 1]; } void modify(int p, int l, int r, int x, const Info &v) { if (r - l == 1) { info[p] = info[p] + v; return; } int m = (l + r) / 2; if (x < m) { modify(2 * p, l, m, x, v); } else { modify(2 * p + 1, m, r, x, v); } pull(p); } void modify(int p, const Info &v) { modify(1, 0, n, p, v); } Info rangeQuery(int p, int l, int r, int x, int y) { if (l >= y || r <= x) { return Info(); } if (l >= x && r <= y) { return info[p]; } int m = (l + r) / 2; return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y); } Info rangeQuery(int l, int r) { return rangeQuery(1, 0, n, l, r); } }; struct Info { i64 sum; }; Info operator+(Info a, Info b) { a.sum += b.sum; return a; } void solve() { int n; std::cin >> n; std::vector<int> a(n); for (auto& x : a) std::cin >> x, x--; i64 ans(0); SegmentTree<Info> T(n + 1); for (int i = n - 1; i >= 0; i--) { ans += T.rangeQuery(0, a[i] + 1).sum;//这里是因为前面对数据做了--防越界处理,而该segmentTree的实现中查询是左闭右开的 T.modify(a[i], {1}); } std::cout << ans << '\n'; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/18058721.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示