[R27F]公约数
数据规模:,。
思路
直接统计 的序列不方便,先看一个更简单的问题:满足「 对每个 都成立」的序列有多少个?此时每个 可以取 ,共 种,所以答案是
而 等价于「不存在质数 整除所有 」。由莫比乌斯反演:
其中 , 是莫比乌斯函数。验证一下: 统计的是「所有 都被 整除」的序列数,反演后恰好留下 恰为 的部分。
直接枚举 是 , 最大 、 最大 ,无法通过。用整除分块优化:把 按「所有 同时不变」分组。对当前块左端点 ,令 ,则块右端点为
块内每个 都等于 (因为 时 ),所以这一块的贡献是
预处理 的前缀和 后, 可 求出。答案对 取模,前缀和差值可能为负,先取模再加模修正。
复杂度
线性筛求 并原地转成前缀和: 时间、 空间(,用 32 位整数数组存,内存约几十 MB)。分块统计共 ,其中块数 ,最坏约 块。总时间约 ,1 秒内轻松通过。
仓颉实现
查看仓颉实现
import std.convert.*
import std.sort.*
import std.collection.*
import std.env.*
const MOD: Int64 = 998244353
main() {
let reader = getStdIn()
let n = Int64.parse(reader.readln().getOrThrow())
var a = reader.readln().getOrThrow().split(" ", removeEmpty: true).map({ p: String => Int64.parse(p) })
sort(a)
let nn = n
let maxA = a[0]
let size = maxA + 1
// 线性筛求莫比乌斯函数 mu,之后原地转成前缀和 M(x) = sum_{d<=x} mu(d)
let mu = Array<Int32>(size, { _ => 0 })
let isComp = Array<Bool>(size, { _ => false })
let primes = ArrayList<Int64>()
mu[1] = 1
var i: Int64 = 2
while (i <= maxA) {
if (!isComp[i]) {
primes.add(i)
mu[i] = -1
}
var j = 0
while (j < primes.size) {
let p = primes[j]
let t = i * p
if (t > maxA) {
break
}
isComp[t] = true
if (i % p == 0) {
mu[t] = 0
break
} else {
mu[t] = -mu[i]
}
j = j + 1
}
i = i + 1
}
var k: Int64 = 1
while (k <= maxA) {
mu[k] = mu[k] + mu[k - 1]
k = k + 1
}
// 对 d 分块:每块内所有 floor(A_i / d) 同时不变,用前缀和快速累加 mu
var ans: Int64 = 0
var d: Int64 = 1
while (d <= maxA) {
var r = maxA
var prod: Int64 = 1
var idx: Int64 = 0
while (idx < nn) {
let ai = a[idx]
let q = ai / d
prod = (prod * (q % MOD)) % MOD
let rq = ai / q
if (rq < r) {
r = rq
}
idx = idx + 1
}
var ms: Int64 = Int64(mu[r]) - Int64(mu[d - 1])
ms = ((ms % MOD) + MOD) % MOD
ans = (ans + ms * prod) % MOD
d = r + 1
}
println(ans)
}