算法学习(22): 逆序对与原序列

这篇具有很好参考价值的文章主要介绍了算法学习(22): 逆序对与原序列。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

逆序对与原序列

在《组合数学》中有这么一个从逆序列构建一个排列的过程……而刚好有一场考试有考了类似的问题,于是在此总结一下。

目录
  • 逆序对与原序列
    • 逆序列
    • 逆序个数
    • 带修改问题
    • 冒泡排序
      • 交换次数
      • 循环次数

逆序列

假定我们有序列 \(P\)\(\{1, 2, \cdots, n\}\) 的一个排列。如果 \(i < j\) 并且 \(p_i > p_j\) 则称数对 \((p_i, p_j)\) 为一个逆序对。

在逆序列中,我们令 \(r_k\) 表示 \(p_j = k\) 的逆序对数量。

注意是数值,而非下标!!!


例子:排列 \(31524\) 的逆序列为 \(1, 2, 0, 1, 0\)

解释:数字 \(1\) 前有一个数比他大,\(2\) 前有两个……所以为 \(1, 2, 0, 1, 0\)


我们不难得知:\(0 \le r_i \le n - i\)。依据乘法原理,我们可知 \(r_i\) 一共有 \(n \times (n-1) \times (n-2) \times \cdots \times 2 \times 1 = n!\) 种。

这提示我们,逆序列与排列一一对应,也就是说,唯一的逆序列可以产生唯一的排列。

那么下面描述两种方法通过逆序列构建一个排列。


算法一:相对插入法

我们令初始序列 \(P\) 为空,逆序考虑 \(k\)\(k\ from\ n \ downto \ 1\)),重复以下步骤:

  • 考虑 \(r_k\),由于 \((k, n]\) 都已经放好,那么可知剩下的数其实对其没有影响。于是我们只需要将 \(k\) 放在第 \(r_k\) 个数后面即可。这样一定可以保证 数 \(k\) 前有 \(r_k\) 个比他大的数。

在这个算法中,我们只做到了这些整数的相对位置不变,但是在排列中的位置是不知道的。

于是我们可以考虑下一个算法。


算法二:绝对插入法

我们先构造 \(n\) 个空位。从 \(1 \sim n\) 考虑 \(r_k\)

  • 因为 \(k\) 前面应该还有 \(r_k\) 个大于 \(k\) 的整数,而且这些数都还没有插进来,因此,必须给这些数留出 \(r_k\) 个空位。于是我们在第 \(r_k + 1\) 的空位上放上数 \(k\)

举个例子:求逆序为 \(1, 2, 0, 1, 0\) 的排列。

算法学习(22): 逆序对与原序列

算法过程也就如此。


说了两种方法,到底哪一种可行性更高?

第一种方法利用 vector 可以很快的写出来,但是复杂度还是近似于 \(O(n^2)\)。利用平衡树可以把他优化到 \(O(n \log n)\) 的复杂度。

而第二种方法,不太好写,朴素还是 \(O(n^2)\) 的复杂度。可以利用线段树和二分做到 \(O(n \log n)\) 的复杂度,只是不太直观。(用树状数组也行,\(O(n \log^2 n)\) 但常数极小。也非常优秀)。


不过如果我们只需要求其中值的位置呢?

我们还是考虑相对插入算法。首先令 \(b = r_k\),表示 \(k\) 在当前时候前面有几个数。

那么能够做出贡献的只有 \(r_i, i \in [1, k)\)。我们模仿插入的过程逆序考虑:

  • 如果 \(r_i \le b\),意味着 \(i\) 会插入到 \(k\) 前面,故令 \(b = b + 1\)

  • 否则不改变 \(b\)

最终 \(b\) 的大小也就是 \(k\) 所在的位置。

这个算法是 \(O(n)\) 的,也启发我们有另外一种 \(\Theta(n^2)\) 的写法。


再来一个问题:求某一个位置的值

这一问题作为练习,留给读者思考(还是考虑 \(O(n)\) 做?)


逆序个数

很多时候,出题人并不会基于这种描述方法,而是采用下标来表示。

逆序个数序列 \(b_i\) 表示 \(\sum_{j < i}[p_j > p_i]\),也就是第 \(i\) 个数前有几个数比它大。

我们可以稍加改变前文中的两个算法,从而得到求逆序的一种方法。

这里讲一下基于相对插入法的做法吧。


相对插入法

还是类似的,令初始序列为空,从前向后考虑下表 \(i\)

  • 我们将 \(i\) 放在从后向前 \(b_i\) 个数前(若 \(b_i = 0\),则直接放在末端)

    注意,是直接放下标

于是最终序列 \(a_i = rank(i)\),也就是 \(i\) 从前向后数的位置。


那么这里考虑某一个位置的值

我们从前向后考虑,令 \(r\) 表示在序列中 \(i\) 后面的数的个数,考虑什么时候 \(b_j\) 会对 \(r\) 做出贡献?若 \(b_j \le r\),那么意味着 \(j\) 会放在 \(i\) 的右侧,那么此时令 \(r = r + 1\)

于是最终 \(a_i = n - r\)

于是我们有了 \(\Theta(n^2)\) 的做法了……


带修改问题

考虑这样一个问题:对于逆序对序列 \(b_i\),要求支持单点修改 \(b_i\),以及单点查询 \(a_i\)

原题为 CF1540D

这道题需要用到分块。

考虑以下事实:

  • 对于一定的序列 \(b_i\),如果初始值为 \(r\),则经过操作后 \(r \to r'\) 的结果是一定的。这启发我们可以分块。

  • 而考虑对于每一个 \(r \in [1, n]\),对应的结果 \(r'\) 一定是不下降的。也就是说 \(r\) 越大,\(r'\) 越大。也就是说我们令 \(f(r)\) 表示 \(r\) 经过了序列之后变成的值,\(f\) 是一个单调不下降序列。

于是对于每一块,考虑用线段树(或者树状数组等神秘数据结构)维护(需要支持区间加和二分求某个值的位置),初始 \(T_i= i\)

按顺序加入 \(b_j\),考虑先找到所有 \(\ge b_j\)\(T_i\) ,并整体加 \(1\)。不难发现,其实每一次只是后缀 \(+1\),又由于原序列的单调的,所以新的序列是单调,这为二分查找提供了保障。

补充一下关于使用树状数组这件事。

复杂度为 \(O(B \log^2 n)\) 是单纯的二分 + 树状数组。然而如果使用树状数组上二分的操作可以避免一个 \(\log\),也就是复杂度成为 \(O(B \log n)\)。这样可以过。

提交:https://codeforces.com/contest/1540/submission/211432601

于是我们可以在 \(O(B \log n)\) 的复杂度中处理出 \(T_i\),并可以在 \(O(\log n)\) 内找到变换对应的值。于是查询的复杂度为 \(O(\frac nB \log n)\)

考虑修改?暴力重构每一块就是了。修改为 \(O(B \log n)\)

块长设为 \(\sqrt n\) 即可,总复杂度为 \(O(n \sqrt n \log n)\)。毕竟是 5s 时限,还是可以过的。

然而会有更优秀的做法,参考:CF1540D. Inverse Inversions - jz_597 - 博客园

虽然我看不懂它……


冒泡排序

这属于是逆序对经典的问题来源了。

由于我太菜,很多东西我可能不知道,如果有什么可以补充或者建设性的建议请留言 QwQ

交换次数

利用冒泡排序将一个无序序列排成有序序列需要多少次交换?

首先观察每次交换中改变的量,很难发现,每次逆序对会减一。

考虑每次交换的是 \(P_i \gt P_{i + 1}\) 的位置,交换之后,对于 \(P_{i + 1}\) 前比它大的数就会减少一个 \(P_i\),而对于 \(P_i\) 没影响,所以总体逆序对会 \(-1\)

于是我们每次排序都可以将逆序对数 \(-1\),于是总交换次数其实就是逆序对数。

循环次数

冒泡排序我们常常有一个小小的优化:

for (int i = 0; i <= n; ++i) {
	bool swaped = false;
	for (int j = 1; j < n; ++j) {
		if (P[i] > P[i + 1]) swap(P[i], P[i + 1]), swaped = true;
	}
	if (!swaped) break;
}

注意 \(i\)\(0\) 开始,这里的循环次数也就是指在最后一行 break\(i\) 的大小,我们怎么求?

其实这里有两个结论:

  • 如果这个序列是一个排列,那么循环次数 \(= \max(i - P_i)\),考虑每次扫一遍,如果 \(P_i < i\),那么这个数必定向前走一步,考虑前面必定有一个 \(> P_i\) 的数会过来与它交换。

  • 对于一般的序列,设 \(inv_i\) 表示第 \(i\) 个数前比他大的数的个数(也就是逆序个数序列),那么循环次数是 \(\max(inv_i)\)。首先不难证明这个结论在排列中与 \(\max(i - P_i)\) 等价,考虑类似的如何在一般序列上证明。如果 \(inv_i > 0\),那么意味着前面一个有一个比它大的数,也就意味着这一次扫一定可以将一个前面的数扫到后面去,使得 \(inv_i \leftarrow inv_i - 1\)。所以扫的次数即 \(inv_i\) 的上界,也就是 \(\max(inv_i)\)

那么我们就可以做 P6186 [NOI Online #1 提高组] 冒泡排序 了。

有一题,需要我们计数循环次数的期望,我们利用这个一般的结论就可以很好的从逆序对序列还原出原序列的个数,从而求得期望了。文章来源地址https://www.toymoban.com/news/detail-466146.html

到了这里,关于算法学习(22): 逆序对与原序列的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数学-排列组合的理解

    排列是有顺序的排队,从 m 中选择 n 个进行排队,第 1 个有 m-0 种选择,第 2 个有 m-1 种选择,自然的,第 n 个有 m-(n-1) 种选择。因为有顺序,可以看出前面的选择,会后面影响后面的选择,所以将选择每个的可能数相乘。 A m n = ( m − 0 ) ∗ ( m − 1 ) ∗ . . . ∗ ( m − ( n − 1

    2023年04月16日
    浏览(44)
  • P3799 妖梦拼木棒(组合数学)

                                                                                                       (学习自用) 提交65.01k 通过15.35k 时间限制1.00s 内存限制125.00MB 上道题中,妖梦斩了一地的木棒,现在她想要将木棒拼起来。 有 n 根木棒,现在从中选 44 根,

    2024年02月01日
    浏览(33)
  • 组合数学——Min-Max容斥

    Min-Max 容斥,即 $$max(S)=sum_{Tin S,Tneqemptyset}(-1)^{|T|-1}min(T)$$ 接下来证明上面那个式子是对的。定义 (S) 中共有 (N) 个元素,由大到小分别为 (s_1,s_2,dots,s_N) , (T_i) 为所有 (S) 大小为 (i) 的子集。 所有元素都大于 (s_i) 且大小为 (j) 的子集有 (tbinom{i-1}{j}) 个;则最

    2024年04月08日
    浏览(40)
  • 【组合数学】【动态规划】【前缀和】1735生成乘积数组的方案数

    【动态规划】【状态压缩】【2次选择】【广度搜索】1494. 并行课程 II 动态规划汇总 C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 组合数学 给你一个二维整数数组 queries ,其中 queries[i] = [ni, ki] 。第 i 个查询 queries[i] 要求构造长度为 ni 、每个

    2024年02月19日
    浏览(52)
  • 青蛙跳台阶(C语言数学排列组合公式求解法)

    题目:从前有一只青蛙他想跳台阶,有n级台阶,青蛙一次可以跳1级台阶,也可以跳2级台阶;问:该青蛙跳到第n级台阶一共有多少种跳法。 当只有跳一级台阶的方法跳时,总共跳n步,共有1次跳法                                 当用了一次跳二级台阶的方法跳时,总共

    2024年02月08日
    浏览(41)
  • 【深度优先搜索】【组合数学】【动态规划】1467.两个盒子中球的颜色数相同的概率

    【动态规划】【字符串】【行程码】1531. 压缩字符串 动态规划汇总 深度优先搜索 组合数学 桌面上有 2n 个颜色不完全相同的球,球上的颜色共有 k 种。给你一个大小为 k 的整数数组 balls ,其中 balls[i] 是颜色为 i 的球的数量。 所有的球都已经 随机打乱顺序 ,前 n 个球放入第

    2024年02月21日
    浏览(39)
  • LeetCode 1359. Count All Valid Pickup and Delivery Options【动态规划,组合数学】1722

    本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,

    2024年02月09日
    浏览(43)
  • 基于组合优化的3D家居布局生成看千禧七大数学难题之NP问题

    本文探讨了运筹学和组合优化方法在3D家居布局生成中的应用,并调研了AI生成3D场景布局的最新方法。文中结合了家居家装业务的实际应用场景,从算法建模和计算复杂度的角度上阐述了室内设计的布局问题中存在的难点,以及如何用简化和近似的思想来建模3D布局生成问题

    2024年02月07日
    浏览(38)
  • Educational Codeforces Round 157 (Rated for Div. 2) F. Fancy Arrays(容斥+组合数学)

    题目 称一个长为n的数列a是fancy的,当且仅当: 1. 数组内至少有一个元素在[x,x+k-1]之间 2. 相邻项的差的绝对值不超过k,即 t(t=50)组样例,每次给定n(1=n=1e9),x(1=x=40), 求fancy的数组的数量,答案对1e9+7取模 思路来源 灵茶山艾府群 官方题解 题解 看到 至少 的字眼,首先想到容斥,

    2024年02月05日
    浏览(40)
  • 2018-2019 ACM-ICPC, Asia Nanjing Regional Contest G. Pyramid(组合数学 计数)

    题目 t(t=1e6)组样例,每次给定一个n(n=1e9),统计边长为n的上述三角形的等边三角形个数 其中等边三角形的三个顶点,可以在所有黑色三角形白色三角形的顶点中任取, 答案对1e9+7取模 思路来源 申老师 oeis A000332 Solution to Problem #3 题解 oeis打一下前四项的表,发现是C(n,4),并且

    2024年02月07日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包