2026 杭电多校第六场的两道数论题
文艺复兴
设 \((a, b) = \gcd (a, b)\)。
Problem C. Phi Master
给定一个序列 \(a_1, a_2, \cdots, a_n(1\le n\le 2\times 10 ^ 6)\),求对于所有满足 \(1\le x \le 10 ^ 7\) 的整数 \(x\),定义:
\[F_x = \max\limits_{1\le i\le n}\varphi(xa_i) \]对于所有 \(1\le x\le 10 ^ 7\),求出 \(F_x\)。
首先有个结论:
那么,对于每个 \(x\),要求的就变成了:
\(\max\) 里面的部分。
现在发现一个问题,就是对于 \(x\) 和 \(a_i\) 呈现倍数关系的 \((x, a_i)\),我们能很简单的得到,但是若不是这样,就很难办。
我们发现式子里面 \({(x, a_i)\over \varphi((x, a_i))}\) 之和 \(x\) 和 \(a_i\) 的 \(\gcd\) 有关(设 \(d = (x, a_i)\))。所以我们只要求出对于每个 \(d\) 求出了最大的 \(\varphi(a_i)\) 应该就能解决问题。
具体来说,我们设 \(t_x = [x\in a_i]\varphi(x)\),然后对 \(t\) 做一个 Dirichlet 后缀和,然后令 \(t_x\leftarrow t_x \times {x\over \varphi(x)}\),再做一个 Dirichlet 前缀和,再令 \(t_x\leftarrow t_x \times\varphi (x)\),这个时候 \(t\) 就是 \(F\)。
但是这里有一个问题,就是这样会不会出现点对 \(x, a_i\),设 \(d=(x, a_i)\),存在 \(k\mid d\),使得:
这样就会让答案偏大。
但是我们知道 \(\varphi(n) = n \prod \frac{p_i - 1}{p_i}\),\(p\) 是 \(n\) 的所有质数因子,那么:
由于 \(k\) 里面的质数 \(d\) 里面肯定有,那么显然有:
所以我们就 \(\mathcal O(n\log\log n)\) 的解决了问题。
#include <bits/stdc++.h>
#define forn(i,s,t) for(int i=(s);i<=(t);++i)
#define form(i,s,t) for(int i=(s);i>=(t);--i)
#define rep(i,s,t) for(int i=(s);i<(t);++i)
using namespace std;
typedef long long ll;
const int N = 1e7 + 2;
int phi[N], p[N], cnt, vis[N];
void table(int n = 1e7) {
phi[1] = 1;
forn (i, 2, n) {
if (!vis[i]) phi[i] = i - 1, p[++cnt] = i;
for (int j = 1; j <= cnt && 1ll * p[j] * i <= n; ++j) {
vis[p[j] * i] = 1;
if (i % p[j] == 0) {
phi[i * p[j]] = phi[i] * p[j];
break ;
}
phi[i * p[j]] = phi[i] * (p[j] - 1);
}
}
}
void solve() {
int n;
cin >> n;
int m = 1e7;
vector<ll> a(m + 1, 0);
rep (i, 0, n) {
int x;
cin >> x;
a[x] = phi[x];
}
forn (i, 1, cnt) for (int j = m / p[i]; j; --j)
a[j] = max(a[j], a[j * p[i]]);
forn (i, 1, m) a[i] = a[i] * i / phi[i];
forn (i, 1, cnt) for (int j = 1; 1ll * j * p[i] <= m; ++j)
a[j * p[i]] = max(a[j * p[i]], a[j]);
forn (i, 1, m) a[i] = phi[i] * a[i];
vector<ll> A(1000, 0);
int B = 1000;
forn (x, 1, m) A[x % B] ^= a[x] * ((x + B - 1) / B);
rep (i, 0, B) cout << A[i] << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int zeg;
cin >> zeg; table();
while (zeg -- ) solve();
return 0;
}
Problem F. GCD Master
给定 \(n\),求 \(\sum\limits_{i = 1} ^ n\sum\limits_{j = i} ^ n\sum\limits_{k = i} ^ j \gcd(i, k)\gcd(j, k){j\choose k}\)。
多测,\(\sum n\le 5\times 10 ^ 5\),时限
10000 ms。
发现题目关于 \(k\) 的东西最多,\(j\) 的第二多,交换求和次序:
设 \(\mathcal F(k) = \sum\limits_{i = 1} ^ k (i, k)\),欧拉反演:\(n = \sum\limits_{d\mid n}\varphi (d)\),得:
这个可以 \(\mathcal O(n)\) 或者 \(\mathcal O(n\log n)\) 求。
原式变为:
设 \(\mathcal G(k) = \sum\limits_{j = k} ^ n {j\choose k} (j, k)\),继续欧拉反演:
展开组合数(设负数的阶乘为 \(0\)):
发现对于每个 \(d\),设 \(a_i = (id)!\),\(b_i = \frac{1}{(id)!}\),那么 \(\sum\limits_{j = 1} ^ {\left\lfloor{ n\over d}\right\rfloor}{(jd)!\over (jd - \frac{k}{d}d)!} = \sum\limits_{j = 1} ^ {\left\lfloor{ n\over d}\right\rfloor} a_j b_{j - {k\over d}}\)。
这是一个差卷积,对于每个 \(d\) 进行 ntt 即可,时间复杂度 \(\mathcal O\left(\sum\limits_{i}{n\over i}\log {n\over i} \right) \le \mathcal O(n\log ^ 2n)\)。
#include <bits/stdc++.h>
#define forn(i,s,t) for(int i=(s);i<=(t);++i)
#define form(i,s,t) for(int i=(s);i>=(t);--i)
#define rep(i,s,t) for(int i=(s);i<(t);++i)
using namespace std;
typedef long long ll;
const int N = 5e5 + 5;
const int Mod = 998244353;
ll q_pow(ll p, ll k = Mod - 2) {
ll r = 1;
for (; k; p = p * p % Mod, k >>= 1)
(k & 1) && (r = r * p % Mod);
return r;
}
ll fac[N], ifac[N], phi[N], F[N];
int pr[N], cnt, vis[N];
#define Poly vector<ll>
const int M = 2e6 + 4;
int tr[M];
#define Poly vector<ll>
inline void table(int n) {
static int lstn = 0;
if (n == lstn) return ;
rep (i, 0, n) tr[i] = (tr[i >> 1] >> 1) | ((i & 1) ? (n >> 1) : 0);
lstn = n;
}
const ll Gmod = 3, iGmod = q_pow(3);
inline void NTT(Poly& f, int n, int fl) {
rep (i, 0, n) if (i < tr[i]) swap(f[i], f[tr[i]]);
for (int p = 2; p <= n; p <<= 1) {
int len = p >> 1;
ll id = q_pow(fl == 1 ? Gmod : iGmod, (Mod - 1) / p);
for (int s = 0; s < n; s += p) {
ll buf = 1;
rep (t, s, s + len) {
ll tmp = buf * f[t + len] % Mod;
f[t + len] = (f[t] - tmp + Mod) % Mod;
f[t] = (f[t] + tmp) % Mod;
buf = buf * id % Mod;
}
}
}
if (fl == -1) {
ll invn = q_pow(n);
rep (i ,0, n) f[i] = f[i] * invn % Mod;
}
}
inline Poly operator * (Poly A, Poly B) {
int n = 1;
while (n < (int)A.size() + (int)B.size())
n <<= 1;
A.resize(n), B.resize(n), table(n);
NTT(A, n, 1), NTT(B, n, 1);
rep (i, 0, n) A[i] = A[i] * B[i] % Mod;
NTT(A, n, -1);
return A;
}
Poly H[N]; int st[N];
void Table(int n = 5e5) {
fac[0] = 1;
forn (i, 1, n) fac[i] = fac[i - 1] * i % Mod;
ifac[n] = q_pow(fac[n]);
form (i, n - 1, 0) ifac[i] = ifac[i + 1] * (i + 1) % Mod;
phi[1] = 1;
forn (i, 2, n) {
if (!vis[i]) phi[i] = i - 1, pr[++cnt] = i;
for (int j = 1; j <= cnt && 1ll * pr[j] * i <= n; ++j) {
vis[pr[j] * i] = 1;
if (i % pr[j] == 0) {
phi[i * pr[j]] = phi[i] * pr[j];
break ;
}
phi[i * pr[j]] = phi[i] * (pr[j] - 1);
}
}
forn (i, 1, n) for (int j = i; j <= n; j += i)
F[j] += phi[i] * j / i, F[j] %= Mod;
}
inline void GetH(int n) {
forn (d, 1, n) {
int m = n / d;
Poly Fc(m + 1, 0), iFc(m + 1, 0);
forn (i, 0, m) {
Fc[i] = fac[i * d];
iFc[m - i] = ifac[i * d];
}
H[d] = Fc * iFc, st[d] = m;
}
}
ll C(int n, int r) {
if (n < 0 || r < 0 || n < r) return 0;
return fac[n] * ifac[r] % Mod * ifac[n - r] % Mod;
}
inline ll bruteforce(int n) {
ll ans = 0;
forn (i, 1, n) forn (j, i, n) forn (k, i, j)
ans += C(j, k) * __gcd(i, k) % Mod * __gcd(j, k) % Mod, ans %= Mod;
return ans;
}
inline ll BruteG(int n, int k) {
ll ans = 0;
forn (j, k, n) ans += C(j, k) * __gcd(j, k) % Mod, ans %= Mod;
return ans;
}
inline ll bruteforce1(int n) {
ll ans = 0;
forn (k, 1, n) ans += F[k] * BruteG(n, k) % Mod, ans %= Mod;
return ans;
}
inline ll Quick(int n) {
GetH(n);
vector<ll> dp(n + 1, 0);
forn (d, 1, n) for (int k = d; k <= n; k += d) {
if ((int)H[d].size() - 1 >= st[d] + k / d)
dp[k] += phi[d] * H[d][st[d] + k / d] % Mod,
dp[k] %= Mod;
}
ll ans = 0;
forn (k, 1, n) ans += F[k] * dp[k] % Mod * ifac[k] % Mod, ans %= Mod;
return ans;
}
void solve() {
int n;
cin >> n;
// cout << bruteforce(n) << '\n';
// cout << bruteforce1(n) << '\n';
cout << Quick(n) << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int zeg;
cin >> zeg; Table();
while (zeg -- ) solve();
return 0;
}

浙公网安备 33010602011771号