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