「解题报告」LOJ561 「LibreOJ Round #9」CommonAnts 的调和数
模拟赛考的题,但是模拟赛没有打,哈哈,摆烂。
考场上想到大致做法了,没继续推,去打 GP of Tokyo 了。
首先发现操作都在查询前面,所以我们只需要预处理出答案即可。
我们先记 \(b_i\) 表示对 \(i\) 进行的操作的总和,那么容易写出 \(a_i\) 的式子:
然后考虑查询操作,也可以写出式子:
发现两个操作都是狄利克雷前缀和 / 后缀和的形式,于是直接暴力计算就能做到 \(O(n \log n) / O(n \log \log n)\)。
考虑题目中给出的限制:所有的操作下标的总质因子个数不超过 \(10\) 个。拿 \(\{2, 3, 5, 7, 11, 13, 17, 19, 23, 29\}\) 打个表,发现只出现这些数为质因子的不超过 \(10^9\) 的数仅有约 \(2 \times 10^5\) 个。这个性质是很优秀的,因为这告诉我们 \(a_i, c_i\) 中有用的数只有 \(2 \times 10^5\) 个。我们记这个数量为 \(V\)。
考虑直接把两个式子揉到一起:
看起来直接做就行了,但是发现问题出在枚举的倍数 \(j\) 上,这个数是可以达到 \(10^9\) 的量级的。但是观察后面的式子,我们枚举的是 \(j\) 的因子,而 \(b_k\) 仅有在 \(k\) 只存在前面的 \(10\) 个质因子的时候有值,这意味着 \(j\) 除了前面的 \(10\) 个质因子之外的因子是对答案不造成任何贡献的。而又因为 \(j^2\) 完全积性的性质,我们可以先枚举所有只存在 \(10\) 个质因子的数,然后再枚举所有不包含 \(10\) 个质因子的数,这样就能把 \(j\) 的贡献拆开了。
具体的,我们设 \(f(n) = \sum_{i = 1}^n i^2 [\gcd(i, p_1 p_2 \cdots p_{10}) = 1]\),\(g_i = \sum_{j | i} \frac{b_j}{j}\),\(S\) 为 \([1, n]\) 中仅存在 \(10\) 个质因子的数的集合,那么上式可以化为:
\(g_j\) 可以直接狄利克雷前缀和计算出来,外层也可以直接狄利克雷后置和计算,现在只需要计算出 \(f(n)\) 即可。注意到 \(f(n)\) 仅会取到 \(O(\sqrt{n})\) 个取值,所以考虑直接预处理出来所有值。
这部分好像做法很多,有一个很暴力的做法就是考虑容斥,\(2^{10}\) 枚举 \(i\) 一定出现过某些质因子,然后计算一下 \(i^2\) 的前缀和即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005, P = 998244353;
int n, m, q;
int p[MAXN], x[MAXN], k[MAXN];
vector<int> fac, s;
int qpow(int a, int b) {
int ans = 1;
while (b) {
if (b & 1) ans = 1ll * ans * a % P;
a = 1ll * a * a % P;
b >>= 1;
}
return ans;
}
const int INV6 = qpow(6, P - 2);
void dfs(int i, int x) {
if (i == fac.size())
s.push_back(x);
else {
for (long long k = 1; x * k <= n; k *= fac[i]) {
dfs(i + 1, x * k);
}
}
}
unordered_map<int, int> a, b, c;
int f(int x) {
x %= P;
return 1ll * x * (x + 1) % P * (2 * x + 1) % P * INV6 % P;
}
int main() {
scanf("%d%d%d", &n, &m, &q);
auto factor = [&](int x) {
for (int i : fac) {
while (x % i == 0) x /= i;
}
if (x != 1) {
for (int i = 2; i * i <= x; i++) {
if (x % i == 0) {
fac.push_back(i);
while (x % i == 0) x /= i;
}
}
if (x != 1) {
fac.push_back(x);
}
}
};
for (int i = 1; i <= m; i++) {
scanf("%d%d", &p[i], &x[i]);
factor(p[i]);
}
for (int i = 1; i <= q; i++) {
scanf("%d", &k[i]);
factor(k[i]);
}
dfs(0, 1);
for (int i = 1; i <= m; i++) {
a[p[i]] = (a[p[i]] + x[i]) % P;
}
sort(fac.begin(), fac.end());
sort(s.begin(), s.end());
for (int x : s) {
a[x] = 1ll * a[x] * qpow(x, P - 2) % P;
}
for (int pri : fac) {
for (int x : s) if (1ll * x * pri <= n) {
a[x * pri] = (a[x * pri] + a[x]) % P;
}
}
for (int x : s) {
int m = n / x;
if (b.count(m)) continue;
for (int t = 0; t < (1 << fac.size()); t++) {
int mul = 1;
for (int i = 0; i < fac.size(); i++) if (t >> i & 1) {
if (1ll * mul * fac[i] > m) {
mul = 0;
break;
}
mul *= fac[i];
}
if (!mul) continue;
b[m] = (b[m] + 1ll * ((__builtin_popcount(t) & 1) ? P - 1ll : 1ll) * mul % P * mul % P * f(m / mul)) % P;
}
}
reverse(s.begin(), s.end());
for (int x : s) {
c[x] = 1ll * b[n / x] * x % P * x % P * a[x] % P;
}
for (int pri : fac) {
for (int x : s) if (x % pri == 0) {
c[x / pri] = (c[x / pri] + c[x]) % P;
}
}
for (int x : s) {
c[x] = 1ll * c[x] * qpow(x, P - 2) % P;
}
for (int i = 1; i <= q; i++) {
printf("%d\n", c[k[i]]);
}
return 0;
}