传送门
给定 n n n, f ( n ) = ∑ i = 1 n ∑ j = 1 n φ ( i j ) φ ( g c d ( i , j ) ) f(n) = \sum_{i=1}^n\sum_{j=1}^n \varphi(ij)\varphi(gcd(i,j)) f(n)=∑i=1n∑j=1nφ(ij)φ(gcd(i,j)),求出 [ 1 , n ] [1,n] [1,n]每个数的 f f f值输出。
这道题有三个难点,我挂在了第一个…
第一步:问题简化
首先式子里的 φ ( g c d ( i , j ) ) \varphi(gcd(i,j)) φ(gcd(i,j))看着很不爽,不按常理出牌,导致没办法直接进行反演变换。于是必须简化,题解直接给出了这个结论:
φ ( i ∗ j ) = φ ( i ) ∗ φ ( j ) ∗ g c d ( i , j ) φ ( g c d ( i , j ) ) \varphi(i*j) = \frac{\varphi(i)*\varphi(j)*gcd(i,j)}{\varphi(gcd(i,j))} φ(i∗j)=φ(gcd(i,j))φ(i)∗φ(j)∗gcd(i,j)
证明上式需要知道欧拉函数的这个性质,设 p p p为素数, φ ( p a ) = p a − p a − 1 \varphi(p^a)=p^a-p^{a-1} φ(pa)=pa−pa−1。简单证明:根据容斥定理,使用 p a p^a pa减去与它不互质的整数个数即可,小于 p a p^a pa且不和 p a p^a pa互质的数的个数就是范围内 p p p的倍数的个数,有 ⌊ p a p ⌋ = p a − 1 \lfloor\frac{p^a}{p} \rfloor=p^{a-1} ⌊ppa⌋=pa−1个,那么定理得证。根据这个定理,欧拉函数也能通过质因数分解求解。
设 i i i的质因数分解式子为 p 1 a 1 p 2 a 2 . . . p n a n p_1^{a_1}p_2^{a_2}...p_n^{a_n} p1a1p2a2...pnan, j j j的质因数分解式子为 q 1 b 1 q 2 b 2 . . . q n b n q_1^{b_1}q_2^{b_2}...q_n^{b_n} q1b1q2b2...qnbn,设 r r r为二者的公共质因子,其中在 i , j i,j i,j中的幂次为 a , b a,b a,b。那么将 i ∗ j i*j i∗j质因数分解然后求欧拉函数得到 r r r部分为 φ ( r a + b ) \varphi(r^{a+b}) φ(ra+b),因此 φ ( r a + b ) = r a + b − r a + b − 1 \varphi(r^{a+b}) = r^{a+b}-r^{a+b-1} φ(ra+b)=ra+b−ra+b−1,互质部分互不影响,那么 φ ( r a ) ∗ φ ( r b ) ∗ g c d ( r a , r b ) φ ( g c d ( r a , r b ) ) = r a + b − r a + b − 1 \frac{\varphi(r^a)*\varphi(r^b)*gcd(r^a,r^b)}{\varphi(gcd(r^a,r^b))} = r^{a+b}-r^{a+b-1} φ(gcd(ra,rb))φ(ra)∗φ(rb)∗gcd(ra,rb)=ra+b−ra+b−1,上式得证。
第二步:反演
∑ i = 1 n ∑ j = 1 n φ ( i j ) φ ( g c d ( i , j ) ) = ∑ i = 1 n ∑ j = 1 n φ ( i ) φ ( j ) g c d ( i , j ) = ∑ i = 1 n ∑ j = 1 n ∑ d = 1 n [ ( i , j ) = d ] ∗ d ∗ φ ( i ) ∗ φ ( j ) \sum_{i=1}^n\sum_{j=1}^n\varphi(ij)\varphi(gcd(i,j)) \\ = \sum_{i=1}^n\sum_{j=1}^n \varphi(i) \varphi(j) gcd(i,j) \\ = \sum_{i=1}^n\sum_{j=1}^n \sum_{d=1}^n [(i,j) = d]*d * \varphi(i) *\varphi(j) ∑i=1n∑j=1nφ(ij)φ(gcd(i,j))=∑i=1n∑j=1nφ(i)φ(j)gcd(i,j)=∑i=1n∑j=1n∑d=1n[(i,j)=d]∗d∗φ(i)∗φ(j)
将枚举 d d d的循环提前:
∑ d = 1 n d ∑ i = 1 n ∑ j = 1 n [ ( i , j ) = 1 ] ∗ φ ( i ) ∗ φ ( j ) \sum_{d=1}^n d \sum_{i=1}^n \sum_{j=1}^n [(i,j) = 1] * \varphi(i)* \varphi(j) ∑d=1nd∑i=1n∑j=1n[(i,j)=1]∗φ(i)∗φ(j)
又因为 ∑ d ∣ ( i , j ) μ ( d ) ⟺ [ ( i , j ) = 1 ] \sum_{d|(i,j)} \mu(d) \Longleftrightarrow [(i,j) = 1] ∑d∣(i,j)μ(d)⟺[(i,j)=1]
∑ d = 1 n d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ n d ⌋ φ ( i ∗ d ) ∗ φ ( j ∗ d ) ∑ k ∣ ( i , j ) μ ( k ) = ∑ d = 1 n d ∑ k = 1 ⌊ n d ⌋ μ ( k ) ∗ [ ∑ i = 1 ⌊ n d k ⌋ φ ( i d k ) ] 2 \sum_{d=1}^n d \sum_{i=1}^{\lfloor \frac{n}{d}\rfloor} \sum_{j=1}^{\lfloor \frac{n}{d}\rfloor} \varphi(i*d)* \varphi(j*d) \sum_{k|(i,j)} \mu(k) \\ =\sum_{d=1}^n d \sum_{k = 1}^{\lfloor \frac{n}{d}\rfloor} \mu(k) *[\sum_{i=1}^{\lfloor \frac{n}{dk}\rfloor} \varphi(idk)]^2 ∑d=1nd∑i=1⌊dn⌋∑j=1⌊dn⌋φ(i∗d)∗φ(j∗d)∑k∣(i,j)μ(k)=∑d=1nd∑k=1⌊dn⌋μ(k)∗[∑i=1⌊dkn⌋φ(idk)]2
令 T = k d , d ∣ T T = kd,d|T T=kd,d∣T,那么改为枚举 T ∈ [ 1 , n ] T \in [1,n] T∈[1,n], ∑ T = 1 n ∑ d ∣ T d ∗ μ ( T d ) ∗ [ ∑ i = 1 ⌊ n T ⌋ φ ( i ∗ T ) ] 2 \sum_{T=1}^n \sum_{d|T}d* \mu(\frac{T}{d}) *[\sum_{i=1}^{\lfloor \frac{n}{T}\rfloor} \varphi(i*T)]^2 ∑T=1n∑d∣Td∗μ(dT)∗[∑i=1⌊Tn⌋φ(i∗T)]2
又因为 φ ( n ) = ∑ d ∣ n μ ( n d ) ∗ d \varphi(n) = \sum_{d|n} \mu(\frac{n}{d})*d φ(n)=∑d∣nμ(dn)∗d,那么最终得到 ∑ T = 1 n φ ( T ) ∗ [ ∑ i = 1 ⌊ n T ⌋ φ ( i ∗ T ) ] 2 \sum_{T=1}^n \varphi(T)* [\sum_{i=1}^{\lfloor \frac{n}{T}\rfloor} \varphi(i*T)]^2 ∑T=1nφ(T)∗[∑i=1⌊Tn⌋φ(i∗T)]2
第三步:求解
但是实际上如果单纯看上式,求出一个数的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),如果求 n n n次显然直接超时,那么就需要找到降低时间复杂度的方法。
观察上式可以得到,对于某个 T T T,那么在 [ i ∗ T , ( i + 1 ) ∗ T ) [i*T, (i+1)*T) [i∗T,(i+1)∗T)中间的结果都是一样的,那么累积的贡献可以相当于区间都加上这个结果,即求差分。那么枚举 [ 1 , m a x n ) [1,maxn) [1,maxn)的每个 T T T,不断地更新每个区间的答案,最后只需要对 [ 1 , n ] [1,n] [1,n]求前缀和还原即可。
#include <bits/stdc++.h>
using namespace std;
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 0x7fffffff;
const int Mod = 1e9 + 7;
const int maxn = 1e6 + 10;
ll ans[maxn];
int phi[maxn], prime[maxn];
bitset<maxn> vis;
void euler() {
phi[1] = 1;
int cnt = 0;
for (int i = 2; i < maxn; i++) {
if (!vis[i]) {
prime[++cnt] = i;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt && 1LL * i * prime[j] < maxn; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j])
phi[i * prime[j]] = (prime[j] - 1) * phi[i];
else {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
}
}
}
void cal(int n) {
for (int i = 1; i <= n; i++) {
ll sum = 0;
for (int j = 1; j <= n / i; j++) {
sum = (sum + phi[j * i]) % Mod;
}
}
}
void solve() {
for (int i = 1; i < maxn; i++) {
ll sum = 0;
for (int l = i, r = 2 * i; l < maxn; l = r, r += i) {
sum = (sum + phi[l]) % Mod;
ans[l] = (ans[l] + sum * sum % Mod * phi[i] % Mod) % Mod;
if (r < maxn)
ans[r] = (ans[r] - sum * sum % Mod * phi[i] % Mod + Mod) % Mod;
}
}
for (int i = 1; i < maxn; i++) {
ans[i] = ((ans[i] + ans[i - 1]) % Mod + Mod) % Mod;
}
}
int main() {
// ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
euler();
solve();
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
printf("%lld\n", ans[i]);
}
return 0;
}