The 2021 CCPC Guangzhou Onsite K Magus Night

题目描述

http://codeforces.com/gym/103415/problem/K

简要题意:给定 $n,m,p,q$,对于一个长度为 $n$ 的序列 $a_i$,$a_i$ 的每一位都是 $[1,m]$,如果整个序列的 $gcd$ 小于等于 $q$,且整个序列的 $lcm$ 大于等于 $p$,那么这个序列是合法的,每个合法序列的价值是 $\prod_{i=1}^na_i$,求所有合法序列的价值的和

$n\le 998244351,p,q\le m\le 2\times 10^5$

Solution

注意到一个序列的 $gcd$ 一定是 $lcm$ 的约数,所以我们考虑直接求 $g_{k}$ 表示 $gcd$ 为 $1$,$lcm$ 为 $k$ 的合法序列的价值的和,这样我们再搞一个调和级数的枚举就能求出所有合法的 $g_{x,y}$

对于 $g_k$,显然我们不太好直接操作,我们考虑求 $f_k=\sum_{d|k} g_d$,即求 $gcd$ 为 $1$,且 $lcm$ 是 $k$ 的约数的合法序列的价值的和,我们尝试列一下 $f_k$ 的式子

显然这个东西我们可以用调和级数的做法算出 $f_1$ 到 $f_m$,然后用作经典的莫比乌斯反演,我们能算出 $g_k$,但是现在我们注意到我们只计算了 $lcm$ 小于等于 $m$ 的情况,但题目实际要求 $lcm$ 大于等于 $p$ 的情况,正难则反,我们求出只有 $gcd$ 的要求下的所有答案,简单 $lcm$ 小于 $p$ 的即可,所有东西的 $n$ 次方都可以预处理,时间复杂度 $O(n\log n)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <algorithm>
#define ll long long
#define maxn 200010
#define INF 1000000000000000000
using namespace std;

const int p = 998244353;

ll pow_mod(ll x, ll n) {
ll s = 1;
for (; n; n >>= 1, x = x * x % p)
if (n & 1) s = s * x % p;
return s;
}

int pri[maxn], cnt, mu[maxn], d[maxn]; bool isp[maxn];
void init_isp(int n) {
mu[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!isp[i]) pri[++cnt] = i, mu[i] = -1;
for (int j = 1; j <= cnt && i * pri[j] <= n; ++j) {
isp[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
mu[i * pri[j]] = -mu[i];
}
}
}

ll n, m, P, Q, f[maxn], g[maxn], a[maxn];
ll in[maxn], dn[maxn], an[maxn];

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> m >> P >> Q; init_isp(200000);
for (int i = 1; i <= m; ++i)
for (int j = i; j <= m; j += i) d[j] += i;
for (int i = 1; i <= m; ++i) in[i] = pow_mod(i, n), dn[i] = pow_mod(d[i], n);
for (int i = 1; i <= m; ++i)
for (int j = i; j <= m; j += i) f[j] = (f[j] + mu[i] * in[i] % p * dn[j / i]) % p;
//for (int i = 1; i <= m; ++i) cout << i << " " << f[i] << "\n"; return 0;
for (int i = 1; i <= m; ++i)
for (int j = i; j <= m; j += i) g[j] = (g[j] + mu[j / i] * f[i]) % p;
//for (int i = 1; i <= m; ++i) cout << i << " " << g[i] << "\n"; return 0;
int ans = 0;
for (int k = 1; k <= P - 1; ++k)
for (int i = 1; i * k < P && i <= Q; ++i) ans = (ans - in[i] * g[k]) % p;
for (int i = 1; i <= m; ++i)
for (int j = 1; j <= m / i; ++j) a[i] = (a[i] + i * j) % p;
for (int i = 1; i <= m; ++i) an[i] = pow_mod(a[i], n);
for (int k = 1; k <= Q; ++k)
for (int d = 1; d <= m / k; ++d) ans = (ans + mu[d] * an[d * k]) % p;
cout << (ans + p) % p << "\n";
return 0;
}