[R37F] 增长药剂
数据规模:,,答案对 取模。
思路
要统计所有 种排列下「喷洒后序列的逆序对总数」。逆序对是二元关系,先固定一对位置 ,考虑这对位置在所有排列中的贡献。
排列均匀地取遍 种情况时, 取到任意一对不同的值 ()的次数都是 。因此位置对 的贡献为:
记 ,则答案为:
注意 与位置无关,只与两个药剂的功效有关,而 ,值域很小。
计算 。先放宽 的限制,统计 :固定 ,满足 的 有 个,于是
对每个 直接 求和即可。再减去对角线上 且 的 个点(仅当 时存在),得到 。
汇总答案。 不能只用每种功效的出现次数算( 是「有序」的,依赖 所在位置在 之前),需要从左到右扫描:维护 表示当前位置之前功效为 的位置数,扫到位置 (功效 )时,累加 ,然后 。最后答案乘上 再取模。
值域只有 ,预计算 个 总代价 ,汇总扫描 。
复杂度
时间 ,空间 。
仓颉实现
查看仓颉实现
import std.convert.*
import std.env.*
const MOD: Int64 = 998244353
main() {
let reader = getStdIn()
let n = Int64.parse(reader.readln().getOrThrow())
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
// C[u][v] = #{x != y : 1 <= x,y <= n, x*u > y*v}, u,v in 1..10
var C = Array<Array<Int64>>(10, { _ => Array<Int64>(10, { _ => 0 }) })
for (u in 1..=10) {
for (v in 1..=10) {
var g: Int64 = 0
for (x in 1..=n) {
let t = (x * u - 1) / v
g += if (t < n) { t } else { n }
}
var c = g
if (u > v) {
c -= n
}
C[u - 1][v - 1] = c
}
}
// S = sum_{i<j} C(a_i, a_j)
var seen = Array<Int64>(10, { _ => 0 })
var s: Int64 = 0
for (j in 0..a.size) {
let v = a[j]
for (u in 1..=10) {
if (seen[u - 1] > 0) {
s = (s + C[u - 1][v - 1] * seen[u - 1]) % MOD
}
}
seen[v - 1] += 1
}
// (n-2)! mod MOD
var fact: Int64 = 1
var k: Int64 = 2
while (k <= n - 2) {
fact = fact * k % MOD
k += 1
}
println((s * fact) % MOD)
}