[R75F] 旋律编排
数据规模:, 是 的排列。
思路
先说明为什么可以数「放置方案」。 处理完 后, 恰好占满整个数组,而 一定是最终排列的某一端。删掉它以后, 仍是连续的一段, 一定是这段的某一端……如此倒着推,每一步都唯一确定。也就是说,给定最终排列 ,放置过程是唯一还原出来的( 时每段至少三个元素,两端不可能相同; 时 也只可能待在一端)。于是「不同的最终排列」与「合法的放置方案」一一对应,直接数方案即可。
把转折条件写成只与位置奇偶有关的形式。 记 中相邻位置 与 的高低关系 。走势不断转折就是 随 交替,即存在固定符号 使得
时没有任何内部位置,任何最终排列都合法,与上式不冲突。
处理 时它们占据连续的一段 ,其中 必在这段的一端。把 接到端元素 的外侧时,新产生的相邻关系就是 与 的关系。无论接在左端(新位置 ,相邻关系落在位置 )还是右端(新位置 ,相邻关系落在位置 ),要求都化为同一个式子
其中 是 在 中的位置。
再把常数项消掉。记 ,并令 。 一定是接在 外侧的,这一步把 定成了 ,于是条件变成
也就是说,判断一步是否合法,只需要知道 和被接的那个端元素相对 的位置奇偶 。
状态设计。 处理完 以后,这一段 的两端分别是 与某个先放入的 ()。设 表示方案数,其中 表示 在左端()还是右端(),。两端距离恰为 ,故 ,记录 的 就够了。
把 接上去时有两个选择:
- 接在 所在的一端:要求 ,新状态 ;
- 接在 所在的一端:要求 ,新状态 。
初始与答案。 接在 左侧或右侧都还没有任何约束,所以 时初始状态为 与 各 种,且 。逐层推到 后,把 的全部状态求和即为答案; 与 时答案分别是 与 。
复杂度
时间 ,空间 。
仓颉实现
查看仓颉实现
import std.env.*
import std.convert.*
const MOD: Int64 = 998244353
main(): Int64 {
let reader = getStdIn()
let n = Int64.parse(reader.readln().getOrThrow())
if (n == 1) {
println(1)
return 0
}
let p = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ s: String => Int64.parse(s) })
// t = sign(p_2 - p_1)
let t: Int64 = if (p[1] > p[0]) { 1 } else { -1 }
// 状态 (j, s, b):p_i 位于 s 端(0 左 / 1 右),另一端是 p_j,
// b = 0 表示 phi_i = +1,b = 1 表示 phi_i = -1
let size = (n + 1) * 4
var f = Array<Int64>(size, { _ => 0 })
var g = Array<Int64>(size, { _ => 0 })
// i = 2 的初始状态:p_2 放在 p_1 左侧或右侧,phi_2 = -1
f[1 * 4 + 1] = 1
f[1 * 4 + 2 + 1] = 1
var i: Int64 = 2
while (i < n) {
let lim = (i + 1) * 4
var k: Int64 = 0
while (k < lim) {
g[k] = 0
k += 1
}
let z = (i - 1) & 1
let px = p[i] // p_{i+1}
let pi = p[i - 1] // p_i
var j: Int64 = 1
while (j < i) {
let pj = p[j - 1]
let base = j * 4
var s: Int64 = 0
while (s < 2) {
var b: Int64 = 0
while (b < 2) {
let v = f[base + s * 2 + b]
if (v != 0) {
// 接在 p_i 所在的一端
let w1 = if (b == 1) { -t } else { t }
let r1 = if (px > pi) { 1 } else { -1 }
if (w1 == r1) {
let idx = base + s * 2 + (1 - b)
let nv = g[idx] + v
g[idx] = if (nv >= MOD) { nv - MOD } else { nv }
}
// 接在 p_j 所在的一端
let bb = b ^ z
let w2 = if (bb == 1) { -t } else { t }
let r2 = if (px > pj) { 1 } else { -1 }
if (w2 == r2) {
let idx = i * 4 + (1 - s) * 2 + (bb ^ 1)
let nv = g[idx] + v
g[idx] = if (nv >= MOD) { nv - MOD } else { nv }
}
}
b += 1
}
s += 1
}
j += 1
}
let tmp = f
f = g
g = tmp
i += 1
}
var ans: Int64 = 0
var j: Int64 = 1
while (j < n) {
let base = j * 4
var k: Int64 = 0
while (k < 4) {
ans += f[base + k]
if (ans >= MOD) {
ans -= MOD
}
k += 1
}
j += 1
}
println(ans)
return 0
}