[R41F]幸运
数据规模:,,。
思路
先看 的分布:恰好有 个前缀最小值(左到右最小值的个数)的 元排列数,等于第一类无符号斯特林数 。这是因为经典的生成函数
(也可以用递推 从「最后一个数是否是新的前缀最小值」推导)。
于是答案为 ,直接求斯特林数表需要 再对每组询问做 求和,无法接受 组询问。需要利用 很小这个关键条件。
把 用第二类斯特林数展开成下降幂:
而 正好是多项式 在 处的 阶导数值 (因为 就是 的 阶导数)。记
由 和莱布尼茨求导法则,得到递推:
边界 , 时 。注意 ,正好对应 时答案为 。
预处理出所有 (,)后,每组询问直接计算:
复杂度
预处理 ,单组询问 ,总复杂度 ,空间 。
仓颉实现
查看仓颉实现
import std.convert.*
import std.env.*
let MOD: Int64 = 998244353
let DMAX: Int64 = 20
let NMAX: Int64 = 100000
let STRIDE: Int64 = NMAX + 1
// A[j * STRIDE + n] = P_n 的 j 阶导数在 x = 1 处的值,
// 其中 P_n(x) = x(x+1)...(x+n-1) = sum_k c(n,k) * x^k(c(n,k) 为第一类无符号斯特林数)
var A = Array<Int64>((DMAX + 1) * STRIDE, { _ => 0 })
// S2[i * (DMAX + 1) + j]:第二类斯特林数
var S2 = Array<Int64>((DMAX + 1) * (DMAX + 1), { _ => 0 })
func solve() {
let reader = getStdIn()
let line = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let n = line[0]
let d = line[1]
var ans: Int64 = 0
var j: Int64 = 0
while (j <= d) {
ans = (ans + S2[d * (DMAX + 1) + j] * A[j * STRIDE + n]) % MOD
j += 1
}
println(ans)
}
main() {
// 第二类斯特林数:i^d = sum_j S2(d, j) * i^(j)
S2[0] = 1
var i: Int64 = 1
while (i <= DMAX) {
var j: Int64 = 1
while (j <= i) {
S2[i * (DMAX + 1) + j] =
(S2[(i - 1) * (DMAX + 1) + (j - 1)] + j * S2[(i - 1) * (DMAX + 1) + j]) % MOD
j += 1
}
i += 1
}
// A[j][n+1] = (n+1) * A[j][n] + j * A[j-1][n],边界 A[0][1] = A[1][1] = 1
A[0 * STRIDE + 1] = 1
A[1 * STRIDE + 1] = 1
var n: Int64 = 1
while (n < NMAX) {
var j: Int64 = 0
while (j <= DMAX) {
var v = (n + 1) * A[j * STRIDE + n]
if (j >= 1) {
v += j * A[(j - 1) * STRIDE + n]
}
A[j * STRIDE + (n + 1)] = v % MOD
j += 1
}
n += 1
}
let reader = getStdIn()
let t = Int64.parse(reader.readln().getOrThrow())
var cnt: Int64 = 0
while (cnt < t) {
solve()
cnt += 1
}
}