[R58C] 猴子排序
数据规模:,,, 为排列。
思路
关键是要判断「交换 与 后逆序对是否减少」。设 ,,,记 为下标在 与 之间、且值严格落在 与 之间的元素个数。交换只影响与位置 、 有关的逆序对:
- 值小于 或大于 的中间元素,交换前后与 、 恰好一增一减,贡献抵消;
- 值落在两者之间的 个元素:若 ,交换前 在它们左侧、 在右侧均不构成逆序对,交换后 移到左侧、 移到右侧各多 个,合计 ;若 则对称地合计 ;
- 与 本身: 时交换后 在 左侧,逆序对 ; 时 。
于是交换前后逆序对变化量:
恒正或恒负,与 无关!因此策略退化为:当且仅当左侧位置的值大于右侧位置的值时交换。注意 没有保证大小关系,先令 ,,若 则交换,否则不交换;交换后继续模拟即可。最后检查排列是否严格递增。
复杂度:时间 ,空间 。
仓颉实现
查看仓颉实现
import std.env.*
import std.convert.*
main() {
let reader = getStdIn()
let nm = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ q: String => Int64.parse(q) })
let n = nm[0]
let m = nm[1]
let p = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ q: String => Int64.parse(q) })
let swapped = Array<Bool>(m, { _ => false })
for (i in 0..m) {
let ab = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ q: String => Int64.parse(q) })
let a = ab[0] - 1
let b = ab[1] - 1
let l = if (a < b) { a } else { b }
let r = if (a < b) { b } else { a }
if (p[l] > p[r]) {
let t = p[l]
p[l] = p[r]
p[r] = t
swapped[i] = true
}
}
var win = true
for (i in 1..n) {
if (p[i] <= p[i - 1]) {
win = false
break
}
}
println(if (win) { "Win" } else { "Lose" })
for (i in 0..m) {
println(if (swapped[i]) { "Yes" } else { "No" })
}
}