The 2021 CCPC Guangzhou Onsite K Magus Night

题目描述

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

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

n998244351,p,qm2×105

Solution

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

对于 gk,显然我们不太好直接操作,我们考虑求 fk=d|kgd,即求 gcd1,且 lcmk 的约数的合法序列的价值的和,我们尝试列一下 fk 的式子

fk=i1=1mi2=1min=1m[(i1,i2,,in)=1][[i1,i2,,in]|k]j=1nij=d|kμ(d)(d|i,i|ki)n=d|kμ(d)(dσ(kd))n=d|kμ(d)dnσn(kd)

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

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;
}