[R37G]烹饪大赛
数据规模:,,。
思路
设选出的集合 大小为 ,其美味程度为 。我们需要对所有非空集合求和。
按 分组。记 为能被 整除的食材个数,则所有元素都被 整除的非空集合有 个,其中每个集合的贡献为 ,于是
而 又可以按 恰好等于 的倍数拆分:,其中 是 恰好为 的集合对 之和。由莫比乌斯反演,答案
最后一步用到了 。
于是流程为:线性筛预处理 ()的欧拉函数 ;预处理 ();对每个 用倍数枚举统计 ,累加 即可。
复杂度
时间 (倍数枚举总和为调和级数 ,),空间 。
仓颉实现
查看仓颉实现
import std.convert.*
import std.env.*
const MOD: Int64 = 998244353
main() {
let reader = getStdIn()
let toks = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
let n = toks[0]
let x = toks[1]
let a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
var M: Int64 = 0
for (v in a) {
if (v > M) {
M = v
}
}
var cnt = Array<Int64>(M + 1, { _ => 0 })
for (v in a) {
cnt[v] = cnt[v] + 1
}
// 线性筛求欧拉函数 phi[1..M]
var phi = Array<Int64>(M + 1, { _ => 0 })
var isComp = Array<Bool>(M + 1, { _ => false })
var primes = Array<Int64>(M + 1, { _ => 0 })
var pc: Int64 = 0
phi[1] = 1
var i: Int64 = 2
while (i <= M) {
if (!isComp[i]) {
primes[pc] = i
pc = pc + 1
phi[i] = i - 1
}
var j: Int64 = 0
while (j < pc) {
let p = primes[j]
let t = i * p
if (t > M) {
break
}
isComp[t] = true
if (i % p == 0) {
phi[t] = phi[i] * p
break
} else {
phi[t] = phi[i] * (p - 1)
}
j = j + 1
}
i = i + 1
}
// (1 + x)^k 预处理,k = 0..n
var pow1x = Array<Int64>(n + 1, { _ => 0 })
pow1x[0] = 1
let base = (x + 1) % MOD
var k: Int64 = 1
while (k <= n) {
pow1x[k] = pow1x[k - 1] * base % MOD
k = k + 1
}
// 对每个 t,统计能被 t 整除的元素个数 c_t,累加 phi[t] * ((1+x)^c_t - 1)
var ans: Int64 = 0
var t: Int64 = 1
while (t <= M) {
var c: Int64 = 0
var m: Int64 = t
while (m <= M) {
c = c + cnt[m]
m = m + t
}
if (c > 0) {
let term = (pow1x[c] - 1 + MOD) % MOD
let add = phi[t] * term % MOD
ans = ans + add
if (ans >= MOD) {
ans = ans - MOD
}
}
t = t + 1
}
println(ans)
}