[R80E] 陈列记录
数据规模:,对每个 都有 。
思路
把大小关系建成有向图: 记为一条边 。只要这张图无环,任取一个拓扑序,把拓扑序中的第 个位置赋成 ,所有边就都成立,由边传递出的路径关系也一并成立。于是任务变成:只加必要的边,保证图始终无环,并让每条记录成立。
第 条记录只涉及位置 。从前往后逐条处理,处理到第 条时, 与 的大小关系已经由之前加的边(直接或传递)确定,只有新位置 与它们的关系需要新加边。用布尔量 记录当前是否有 。
第 条记录决定了 :若 ,则 是中位数,无论 比 大还是小,都有 ,所以 ,加边 ;若 ,即 ,同理都有 ,所以 ,加边 。这里的相对顺序是被输入唯一确定的,不是任取。
下面处理第 条记录。记 ,,, 指明中位数是三者中的哪一个。
- ,即 是中位数: 必须夹在 与 之间。若 为真(),加边 、;否则加边 、。
- ,即 是最大或最小的那个:若 为真,,那么 时三元组只能是 ( 最大), 时只能是 ( 最小);若 为假,三种取值同理全部确定。此时只需把 与 、 都连边: 最大就加 、, 最小就加 、。
两条分支可以合并: 同时大于 与 ,当且仅当 ;处理完这条记录后,新的 (即 )在 时保持不变( 夹在中间, 与 同真假),在 时取反( 跑到 的另一侧)。
正确性
- 图始终无环。 每一步新加的边都只把新位置 与 、 相连,并且这些边描述的都是同一个全序( 最小、 居中或 最大三种情况之一)。假设处理前图有一个线性序,按 在三种情况中对应的位置把它插入到 、 之间或两端,就得到新图的一个线性序。线性序存在则无环,归纳可知整张图始终无环。
- 每条记录都被满足。 处理第 条记录时, 与 的旧关系已知,新加的两条边再定下 的位置,三者的大小顺序已经完整确定, 恰是中位数;剩下的那一组比较由已加边的传递性推出。同时新的 正确记录了 与 的关系,供下一条记录使用。
- 后续步骤不会破坏前面的记录。 后面的记录只会引入新位置并增加边,不会删除或反向任何已有边;最终的赋值是整个图的线性序,因此前面各条记录涉及的三元组大小关系原样保持。
所以对任何合法输入都一定能构造出排列,不存在输出 的情况。最后对整张图跑一遍拓扑排序,按出队顺序把位置赋成 即可。
复杂度
时间 ,空间 。边数至多 。
仓颉实现
查看仓颉实现
import std.env.*
import std.convert.*
import std.collection.*
func addEdge(edges: Array<ArrayList<Int64>>, indeg: Array<Int64>, a: Int64, b: Int64) {
edges[a].add(b)
indeg[b] += 1
}
main() {
let reader = getStdIn()
let n = Int64.parse(reader.readln().getOrThrow())
let m = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ s: String => Int64.parse(s) })
let size = n + 1
let edges = Array<ArrayList<Int64>>(size, { _ => ArrayList<Int64>() })
let indeg = Array<Int64>(size, { _ => 0 })
var increasing = m[0] != 1
if (increasing) {
addEdge(edges, indeg, 1, 2)
} else {
addEdge(edges, indeg, 2, 1)
}
for (i in 1..=n - 2) {
let x = i
let y = i + 1
let z = i + 2
let middle = m[i - 1] - i
if (middle == 2) {
if (increasing) {
addEdge(edges, indeg, x, z)
addEdge(edges, indeg, z, y)
} else {
addEdge(edges, indeg, y, z)
addEdge(edges, indeg, z, x)
}
} else {
let newIsLarger = (middle == 1) == increasing
if (newIsLarger) {
addEdge(edges, indeg, x, z)
addEdge(edges, indeg, y, z)
} else {
addEdge(edges, indeg, z, x)
addEdge(edges, indeg, z, y)
}
}
if (middle != 1) {
increasing = !increasing
}
}
let ans = Array<Int64>(size, { _ => 0 })
let queue = Array<Int64>(size, { _ => 0 })
var head = 0
var tail = 0
for (i in 1..=n) {
if (indeg[i] == 0) {
queue[tail] = i
tail += 1
}
}
while (head < tail) {
let v = queue[head]
ans[v] = head + 1
for (u in edges[v]) {
indeg[u] -= 1
if (indeg[u] == 0) {
queue[tail] = u
tail += 1
}
}
head += 1
}
for (i in 1..=n) {
if (i > 1) {
print(" ")
}
print(ans[i])
}
println()
}