题意:
给定一个整数 n n n,让我们求出表达式 ∑ i = 1 n g c d ( i , n ) \sum_{i=1}^ngcd(i,n) ∑i=1ngcd(i,n)的值。
题解:
首先,我们可以发现这个最大公约数一定是
n
n
n的约数。因此我们可以进行分类,也就是枚举
n
n
n的约数。然后原来的表达式就可以改写成:
∑
d
∣
n
d
∗
\sum_{d|n}d*
∑d∣nd∗最大公约数为d的个数,也就是有多少个
x
x
x满足
g
c
d
(
x
,
n
)
=
=
d
gcd(x,n)==d
gcd(x,n)==d。这个个数该怎么求?
我们发现,如果有
g
c
d
(
x
,
n
)
=
=
d
gcd(x,n)==d
gcd(x,n)==d,那么
g
c
d
(
x
d
,
n
d
)
=
=
1
gcd(\frac xd,\frac nd)==1
gcd(dx,dn)==1。 其实这个个数也就是
ϕ
(
n
d
)
\phi(\frac nd)
ϕ(dn)。
于是上面的表达式就是 ∑ d ∣ n d ∗ ϕ ( n d ) \sum_{d|n}d*\phi(\frac nd) ∑d∣nd∗ϕ(dn)。
推出上面这个公式,已经可以进行求解了。时间复杂度为 O ( 1600 n ) O(1600\sqrt n) O(1600n)
不过时间复杂度可以进一步优化到 O ( n ) O(\sqrt n) O(n)。
我们对上面的表达式进一步化简:
∑ d ∣ n d ∗ n d ∗ ∏ i = 1 k d ( 1 − 1 p i ) = n ∗ ∑ d ∣ n ∏ i = 1 k d ( 1 − 1 p i ) \sum_{d|n}d*\frac nd*\prod_{i=1}^{k_d}(1-\frac 1{p_i})=n*\sum_{d|n}\prod_{i=1}^{k_d}(1-\frac 1{p_i}) ∑d∣nd∗dn∗∏i=1kd(1−pi1)=n∗∑d∣n∏i=1kd(1−pi1)。
怎么求这个积呢?
假设
n
=
p
1
α
1
p
2
α
2
…
p
k
α
k
n=p_1^{\alpha_1}p_2^{\alpha_2}\dots p_k^{\alpha_k}
n=p1α1p2α2…pkαk,那么它的一个因子可以表示为:
d
=
p
1
β
1
p
2
β
2
…
p
k
β
k
,
0
≤
β
i
≤
α
i
d=p_1^{\beta_1}p_2^{\beta_2}\dots p_k^{\beta_k},0\leq\beta_i\leq\alpha_i
d=p1β1p2β2…pkβk,0≤βi≤αi
那么我们可以对每个质因子依次判断,那么所有的约数可以表示出来:
(
p
1
0
+
p
1
1
+
⋯
+
p
1
α
1
)
(
p
2
0
+
p
2
1
+
⋯
+
p
2
α
2
)
…
(
p
k
0
+
p
k
1
+
⋯
+
p
k
α
k
)
(p_1^0+p_1^1+\dots +p_1^{\alpha_1})(p_2^0+p_2^1+\dots +p_2^{\alpha_2})\dots (p_k^0+p_k^1+\dots +p_k^{\alpha_k})
(p10+p11+⋯+p1α1)(p20+p21+⋯+p2α2)…(pk0+pk1+⋯+pkαk)
那么 n n n的所有约数都可以从上面每个括号中选择一项组成。
那么这个
∑
d
∣
n
∏
i
=
1
k
d
(
1
−
1
p
i
)
\sum_{d|n}\prod_{i=1}^{k_d}(1-\frac 1{p_i})
∑d∣n∏i=1kd(1−pi1)表达式的值可以根据每个质因子的选择情况求出,也就是:
(
1
+
(
1
−
1
p
1
)
+
⋯
+
(
1
−
1
p
1
)
)
∗
(
1
+
(
1
−
1
p
2
)
+
⋯
+
(
1
−
1
p
2
)
)
∗
⋯
=
∑
i
=
1
k
(
1
+
α
i
(
1
−
1
p
i
)
)
(1+(1-\frac 1{p_1})+\dots +(1-\frac 1{p_1}))*(1+(1-\frac 1{p_2})+\dots +(1-\frac 1{p_2}))*\dots=\\\sum_{i=1}^k(1+\alpha_i(1-\frac 1{p_i}))
(1+(1−p11)+⋯+(1−p11))∗(1+(1−p21)+⋯+(1−p21))∗⋯=∑i=1k(1+αi(1−pi1))
这样这道题目的就可以在 O ( n ) O(\sqrt n) O(n)解决。
实现细节见代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll n;
cin >> n;
ll ans = n;
for (ll i = 2; i * i <= n; i++) {
ll now = 0, p = i;
if (n % p == 0) {
while (n % p == 0) {
n /= p;
now++;
}
ans /= p;
ans *= (p + now * p - now);
}
}
if (n > 1) {
ans /= n;
ans *= (n + n - 1);
}
cout << ans << endl;
return 0;
}