Description
-
多测, t t t 组数据。
-
每次给定两个整数 n , m n, m n,m,请求出
[ ∑ i = 1 n ∑ j = 1 m φ ( i j ) ] m o d 998244353 \left[ \sum_{i = 1}^n \sum_{j = 1}^m \varphi(ij) \right] \bmod 998244353 [i=1∑nj=1∑mφ(ij)]mod998244353 -
1 ≤ t ≤ 1 0 4 1\le t\le 10^4 1≤t≤104, 1 ≤ n , m ≤ 1 0 5 1\le n, m\le 10^5 1≤n,m≤105。
Solution
引理:
φ
(
i
j
)
=
φ
(
i
)
φ
(
j
)
gcd
(
i
,
j
)
φ
(
gcd
(
i
,
j
)
)
\varphi(ij) = \dfrac{\varphi(i) \varphi(j) \gcd(i, j)}{\varphi(\gcd(i, j))}
φ(ij)=φ(gcd(i,j))φ(i)φ(j)gcd(i,j)
证明:
φ ( i ) φ ( j ) gcd ( i , j ) = i ∏ p 1 ∈ P , p 1 ∣ i p 1 − 1 p 1 ⋅ j ∏ p 2 ∈ P , p 2 ∣ j p 2 − 1 p 2 ⋅ gcd ( i , j ) = i j gcd ( i , j ) ( ∏ p 1 ∈ P , p 1 ∣ i p 1 − 1 p 1 ∏ p 2 ∈ P , p 2 ∣ j p 2 − 1 p @ ) \begin{aligned} \varphi(i) \varphi(j) \gcd(i, j) & = i \prod_{p_1\in \mathbb{P}, p_1\mid i} \dfrac{p_1 - 1}{p_1} \cdot j \prod_{p_2\in \mathbb{P}, p_2\mid j} \dfrac{p_2 - 1}{p_2} \cdot \gcd(i, j) \\ & = ij \gcd(i, j) \left(\prod_{p_1\in \mathbb{P}, p_1\mid i} \dfrac{p_1 - 1}{p_1} \prod_{p_2\in \mathbb{P}, p_2\mid j} \dfrac{p_2 - 1}{p_@} \right) \end{aligned} φ(i)φ(j)gcd(i,j)=ip1∈P,p1∣i∏p1p1−1⋅jp2∈P,p2∣j∏p2p2−1⋅gcd(i,j)=ijgcd(i,j)⎝⎛p1∈P,p1∣i∏p1p1−1p2∈P,p2∣j∏p@p2−1⎠⎞
你会发现所有 gcd ( i , j ) \gcd(i, j) gcd(i,j) 的质因数都被算了 2 2 2 次。
= ( i j ∏ p 1 ∈ P , p 1 ∣ i j p 1 − 1 p 1 ) ⋅ ( gcd ( i , j ) ∏ p 2 ∈ P , p 2 ∣ gcd ( i , j ) p 2 − 1 p 2 ) = φ ( i j ) φ ( gcd ( i , j ) ) \begin{aligned} & = \left(ij\prod_{p_1\in \mathbb{P}, p_1\mid ij} \dfrac{p_1 - 1}{p_1} \right) \cdot \left(\gcd(i, j) \prod_{p_2\in \mathbb{P}, p_2\mid \gcd(i, j)} \dfrac{p_2 - 1}{p_2} \right) \\ & = \varphi(ij) \varphi(\gcd(i, j)) \end{aligned} =⎝⎛ijp1∈P,p1∣ij∏p1p1−1⎠⎞⋅⎝⎛gcd(i,j)p2∈P,p2∣gcd(i,j)∏p2p2−1⎠⎞=φ(ij)φ(gcd(i,j))
把 φ ( gcd ( i , j ) ) \varphi(\gcd(i, j)) φ(gcd(i,j)) 除过去即可。
不妨设
n
≤
m
n\le m
n≤m,根据引理,有
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
φ
(
i
)
φ
(
j
)
gcd
(
i
,
j
)
φ
(
gcd
(
i
,
j
)
)
=
∑
d
=
1
n
∑
i
=
1
n
∑
j
=
1
m
φ
(
i
)
φ
(
j
)
d
φ
(
d
)
[
gcd
(
i
,
j
)
=
d
]
=
∑
d
=
1
n
d
φ
(
d
)
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
φ
(
i
d
)
φ
(
j
d
)
[
gcd
(
i
,
j
)
=
1
]
=
∑
d
=
1
n
d
φ
(
d
)
∑
i
=
1
⌊
n
d
⌋
φ
(
i
d
)
∑
j
=
1
⌊
m
d
⌋
φ
(
j
d
)
[
gcd
(
i
,
j
)
=
1
]
=
∑
d
=
1
n
d
φ
(
d
)
∑
i
=
1
⌊
n
d
⌋
φ
(
i
d
)
∑
j
=
1
⌊
m
d
⌋
φ
(
j
d
)
∑
k
∣
gcd
(
i
,
j
)
μ
(
k
)
=
∑
d
=
1
n
d
φ
(
d
)
∑
k
=
1
⌊
n
d
⌋
μ
(
k
)
∑
i
=
1
⌊
n
d
k
⌋
φ
(
i
d
k
)
∑
j
=
1
⌊
m
d
k
⌋
φ
(
j
d
k
)
=
∑
T
=
1
n
∑
d
∣
T
d
φ
(
d
)
μ
(
T
d
)
∑
i
=
1
⌊
n
T
⌋
φ
(
i
T
)
∑
j
=
1
⌊
m
T
⌋
φ
(
j
T
)
\begin{aligned} ans & = \sum_{i = 1}^n \sum_{j = 1}^m \dfrac{\varphi(i) \varphi(j) \gcd(i, j)}{\varphi(\gcd(i, j))} \\ & = \sum_{d = 1}^n \sum_{i = 1}^n \sum_{j = 1}^m \dfrac{\varphi(i) \varphi(j) d}{\varphi(d)} [\gcd(i, j) = d] \\ & = \sum_{d = 1}^n \dfrac{d}{\varphi(d)} \sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor} \varphi(id) \varphi(jd) [\gcd(i, j) = 1] \\ & = \sum_{d = 1}^n \dfrac{d}{\varphi(d)} \sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \varphi(id) \sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor} \varphi(jd) [\gcd(i, j) = 1] \\ & = \sum_{d = 1}^n \dfrac{d}{\varphi(d)} \sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \varphi(id) \sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor} \varphi(jd) \sum_{k\mid \gcd(i, j)} \mu(k) \\ & = \sum_{d = 1}^n \dfrac{d}{\varphi(d)} \sum_{k = 1}^{\left\lfloor\frac{n}{d}\right\rfloor} \mu(k) \sum_{i = 1}^{\left\lfloor\frac{n}{dk}\right\rfloor} \varphi(idk) \sum_{j = 1}^{\left\lfloor\frac{m}{dk}\right\rfloor} \varphi(jdk) \\ & = \sum_{T = 1}^n \sum_{d\mid T} \dfrac{d}{\varphi(d)} \mu\left(\dfrac{T}{d}\right) \sum_{i = 1}^{\left\lfloor\frac{n}{T}\right\rfloor} \varphi(iT) \sum_{j = 1}^{\left\lfloor\frac{m}{T}\right\rfloor} \varphi(jT) \end{aligned}
ans=i=1∑nj=1∑mφ(gcd(i,j))φ(i)φ(j)gcd(i,j)=d=1∑ni=1∑nj=1∑mφ(d)φ(i)φ(j)d[gcd(i,j)=d]=d=1∑nφ(d)di=1∑⌊dn⌋j=1∑⌊dm⌋φ(id)φ(jd)[gcd(i,j)=1]=d=1∑nφ(d)di=1∑⌊dn⌋φ(id)j=1∑⌊dm⌋φ(jd)[gcd(i,j)=1]=d=1∑nφ(d)di=1∑⌊dn⌋φ(id)j=1∑⌊dm⌋φ(jd)k∣gcd(i,j)∑μ(k)=d=1∑nφ(d)dk=1∑⌊dn⌋μ(k)i=1∑⌊dkn⌋φ(idk)j=1∑⌊dkm⌋φ(jdk)=T=1∑nd∣T∑φ(d)dμ(dT)i=1∑⌊Tn⌋φ(iT)j=1∑⌊Tm⌋φ(jT)
其中
f
(
n
)
=
∑
d
∣
n
d
μ
(
n
d
)
φ
(
d
)
f(n) = \sum_{d\mid n} \dfrac{d\, \mu\left(\dfrac{n}{d}\right)}{\varphi(d)}
f(n)=d∣n∑φ(d)dμ(dn)
不是积性函数(它甚至不一定是整数),直接预处理逆元
Θ
(
n
ln
n
)
\Theta(n\ln n)
Θ(nlnn) 计算。
对于
∑
i
=
1
⌊
n
T
⌋
φ
(
i
T
)
∑
j
=
1
⌊
m
T
⌋
φ
(
j
T
)
\sum_{i = 1}^{\left\lfloor\frac{n}{T}\right\rfloor} \varphi(iT) \sum_{j = 1}^{\left\lfloor\frac{m}{T}\right\rfloor} \varphi(jT)
i=1∑⌊Tn⌋φ(iT)j=1∑⌊Tm⌋φ(jT)
发现这东西带两个参数,令
g
(
k
,
n
)
=
∑
i
=
1
n
φ
(
i
k
)
g(k, n) = \sum_{i = 1}^n \varphi(ik)
g(k,n)=i=1∑nφ(ik)
你有递推式
g
(
k
,
n
)
=
g
(
k
,
n
−
1
)
+
φ
(
n
k
)
g(k, n) = g(k, n - 1) + \varphi(nk)
g(k,n)=g(k,n−1)+φ(nk)
而此处的
n
k
≤
n
(题目中的
n
)
nk\le n\textsf{(题目中的 } n \textsf{)}
nk≤n(题目中的 n),也可以
Θ
(
n
ln
n
)
\Theta(n\ln n)
Θ(nlnn) 预处理,
g
g
g 数组用 vector
存。
代回去
a
n
s
=
∑
T
=
1
n
f
(
T
)
g
(
T
,
⌊
n
T
⌋
)
g
(
T
,
⌊
m
T
⌋
)
ans = \sum_{T = 1}^n f(T) g\left(T, \left\lfloor\dfrac{n}{T}\right\rfloor \right) g\left(T, \left\lfloor\dfrac{m}{T}\right\rfloor \right)
ans=T=1∑nf(T)g(T,⌊Tn⌋)g(T,⌊Tm⌋)
这个
g
g
g 带一个
T
T
T,无法整除分块,只能暴力算。
此时是 O ( n ln n ) \Omicron(n\ln n) O(nlnn) 的预处理, O ( n ) \Omicron(n) O(n) 的单次回答,过不去。
设
h
(
n
,
m
,
l
)
=
∑
T
=
1
l
f
(
T
)
g
(
T
,
n
)
g
(
T
,
m
)
h(n, m, l) = \sum_{T = 1}^l f(T) g\left(T, n \right) g\left(T, m \right)
h(n,m,l)=T=1∑lf(T)g(T,n)g(T,m)
这样在整除分块中
h
(
⌊
n
l
⌋
,
⌊
m
l
⌋
,
r
)
−
h
(
⌊
n
l
⌋
,
⌊
m
l
⌋
,
l
−
1
)
h\left(\left\lfloor\dfrac{n}{l}\right\rfloor, \left\lfloor\dfrac{m}{l}\right\rfloor, r \right) - h\left(\left\lfloor\dfrac{n}{l}\right\rfloor, \left\lfloor\dfrac{m}{l}\right\rfloor, l - 1 \right)
h(⌊ln⌋,⌊lm⌋,r)−h(⌊ln⌋,⌊lm⌋,l−1)
又有递推式
h
(
n
,
m
,
l
)
=
h
(
n
,
m
,
l
−
1
)
+
f
(
l
)
g
(
l
,
n
)
g
(
l
,
m
)
h(n, m, l) = h(n, m, l - 1) + f(l) g(l, n) g(l, m)
h(n,m,l)=h(n,m,l−1)+f(l)g(l,n)g(l,m)
有
max
(
n
,
m
)
l
≤
n
(题目中的
n
)
\max(n, m) l\le n \textsf{(题目中的 } n \textsf{)}
max(n,m)l≤n(题目中的 n)。
h
h
h 数组同样用 vector
存。
此时是
O
(
n
∑
i
=
1
n
n
i
)
=
O
(
n
⋅
∫
1
n
n
x
d
x
)
=
O
(
n
2
ln
n
)
\begin{aligned} \Omicron\left(n \sum_{i = 1}^n \dfrac{n}{i} \right) & = \Omicron\left(n \cdot \int_1^n \dfrac{n}{x} \, dx \right) \\ & = \Omicron(n^2 \ln n) \end{aligned}
O(ni=1∑nin)=O(n⋅∫1nxndx)=O(n2lnn)
的预处理,
O
(
n
)
\Omicron(\sqrt{n})
O(n) 的单次回答,预处理会
MLE
+
TLE
\text{MLE} + \text{TLE}
MLE+TLE。
既然两种方法都不行,那么自然想到 用根号分治来平衡时间。
设阈值为 k k k,表示对于 ≤ k \le k ≤k 的部分预处理。
于是当 ⌊ m l ⌋ ≤ k \left\lfloor\dfrac{m}{l}\right\rfloor \le k ⌊lm⌋≤k,即 l > ⌊ m k ⌋ l > \left\lfloor\dfrac{m}{k}\right\rfloor l>⌊km⌋ 的部分用已经预处理过的 h h h 整除分块算,当 l ≤ ⌊ m k ⌋ l \le \left\lfloor\dfrac{m}{k}\right\rfloor l≤⌊km⌋ 时直接暴力算。
- 暴力: O ( n ln n ) + O ( t n k ) \Omicron(n\ln n) + \Omicron\left(\dfrac{tn}{k} \right) O(nlnn)+O(ktn)
- 分块: O ( n k ln k ) + O ( t n ) \Omicron(nk \ln k) + \Omicron(t\sqrt{n}) O(nklnk)+O(tn)
总时间复杂度为 O ( n k ln k + t ( n k + n ) ) \Omicron\left(nk \ln k + t\left(\dfrac{n}{k} + \sqrt{n} \right) \right) O(nklnk+t(kn+n)),提个公因数就是 k ln k + t k k\ln k + \dfrac{t}{k} klnk+kt 最小。
写个程序去算
int t = 1e4;
int ans = 0x3f3f3f3f, pos = 0;
for (int k = 1; k <= t; k++)
{
int res = k * log(k) + t / k;
if (res < ans)
{
ans = res, pos = k;
}
}
printf("%d %d\n", ans, pos);
输出
392 47
所以取 k = 47 k = 47 k=47。
空间复杂度为 O ( n k ln k ) \Omicron(nk\ln k) O(nklnk)。
然而实测 k k k 取 60 ∼ 70 60\sim 70 60∼70 更快(
Code
// 18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#include <vector>
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;
const int MAXN = 1e5 + 5;
const int N = 1e5;
const int MAXK = 47 + 5;
const int K = 47;
const int MOD = 998244353;
typedef int arr[MAXN];
int add(int a, int b)
{
return (a + b) % MOD;
}
int sub(int a, int b)
{
return (a - b + MOD) % MOD;
}
int mul(int a, int b)
{
return (ll)a * b % MOD;
}
int qpow(int a, int b)
{
int base = a, ans = 1;
while (b)
{
if (b & 1)
{
ans = (ll)ans * base % MOD;
}
base = (ll)base * base % MOD;
b >>= 1;
}
return ans;
}
int inv(int a)
{
return qpow(a, MOD - 2);
}
arr p, mu, phi, phi_pro, phi_pro_inv, phi_inv, f;
bool vis[MAXN];
vector<int> g[MAXN], h[MAXK][MAXK];
void pre()
{
mu[1] = phi[1] = 1;
for (int i = 2; i <= N; i++)
{
if (!vis[i])
{
p[++p[0]] = i;
mu[i] = MOD - 1;
phi[i] = i - 1;
}
for (int j = 1; j <= p[0] && i * p[j] <= N; j++)
{
vis[i * p[j]] = true;
if (i % p[j] == 0)
{
mu[i * p[j]] = 0;
phi[i * p[j]] = phi[i] * p[j];
break;
}
mu[i * p[j]] = MOD - mu[i];
phi[i * p[j]] = phi[i] * phi[p[j]];
}
}
phi_pro[0] = phi_inv[1] = 1;
for (int i = 1; i <= N; i++)
{
phi_pro[i] = mul(phi_pro[i - 1], phi[i]);
}
phi_pro_inv[N] = inv(phi_pro[N]);
for (int i = N - 1; i >= 1; i--)
{
phi_pro_inv[i] = mul(phi_pro_inv[i + 1], phi[i + 1]);
phi_inv[i + 1] = mul(phi_pro_inv[i + 1], phi_pro[i]);
}
for (int i = 1; i <= N; i++)
{
for (int j = 1; i * j <= N; j++)
{
f[i * j] = add(f[i * j], mul(mul(i, mu[j]), phi_inv[i]));
}
}
for (int k = 1; k <= N; k++)
{
g[k].resize(N / k + 5);
for (int n = 1; n * k <= N; n++)
{
g[k][n] = add(g[k][n - 1], phi[n * k]);
}
}
for (int n = 1; n <= K; n++)
{
for (int m = n; m <= K; m++)
{
h[n][m].resize(N / m + 5);
for (int l = 1; m * l <= N; l++)
{
h[n][m][l] = add(h[n][m][l - 1], mul(f[l], mul(g[l][n], g[l][m])));
}
}
}
}
int block(int n, int m)
{
int res = 0;
for (int i = 1; i <= m / K; i++)
{
res = add(res, mul(f[i], mul(g[i][n / i], g[i][m / i])));
}
for (int l = m / K + 1, r; l <= n; l = r + 1)
{
int k1 = n / l, k2 = m / l;
r = min(n / k1, m / k2);
res = add(res, sub(h[k1][k2][r], h[k1][k2][l - 1]));
}
return res;
}
int main()
{
pre();
int t;
scanf("%d", &t);
while (t--)
{
int n, m;
scanf("%d%d", &n, &m);
if (n > m)
{
swap(n, m);
}
printf("%d\n", block(n, m));
}
return 0;
}