题目大意
求 ∑ i = 1 n l c m ( i , n ) \sum_{i=1}^nlcm(i,n) ∑i=1nlcm(i,n)
反演
根据常见套路,首先我们化成含 g c d gcd gcd的式子: ∑ i = 1 n i ∗ n g c d ( i , n ) \sum_{i=1}^n\frac{i*n}{gcd(i,n)} ∑i=1ngcd(i,n)i∗n
然后枚举 g c d ( i , n ) gcd(i,n) gcd(i,n),得到 n ∑ i = 1 n ∑ d = 1 , d ∣ n n i d [ g c d ( i , n ) = d ] n\sum_{i=1}^n\sum_{d=1,d|n}^n\frac{i}{d}[gcd(i,n)=d] n∑i=1n∑d=1,d∣nndi[gcd(i,n)=d]
再根据 [ g c d ( i d , n d ) = 1 ] ⇔ [ g c d ( i , n ) = d ] [gcd(\frac{i}{d},\frac{n}{d})=1] \Leftrightarrow [gcd(i,n)=d] [gcd(di,dn)=1]⇔[gcd(i,n)=d]
令 i = x ∗ d i=x*d i=x∗d,枚举 x ∈ [ 1 , n ] x\in [1,n] x∈[1,n],代入 i i i后刚好消去了 1 i \frac{1}{i} i1,得到 n ∑ d ∣ n ∑ x = 1 ⌊ n d ⌋ x ∗ [ g c d ( x , ⌊ n d ⌋ ) = 1 ] n\sum_{d|n} \sum_{x=1}^{\lfloor \frac{n}{d}\rfloor} x*[gcd(x,\lfloor \frac{n}{d}\rfloor)=1] n∑d∣n∑x=1⌊dn⌋x∗[gcd(x,⌊dn⌋)=1]
欧拉函数性质
∑ x = 1 ⌊ n d ⌋ x ∗ [ g c d ( x , ⌊ n d ⌋ ) = 1 ] \sum_{x=1}^{\lfloor \frac{n}{d}\rfloor} x*[gcd(x,\lfloor \frac{n}{d}\rfloor)=1] ∑x=1⌊dn⌋x∗[gcd(x,⌊dn⌋)=1]即小于 ⌊ n d ⌋ \lfloor \frac{n}{d}\rfloor ⌊dn⌋且与 ⌊ n d ⌋ \lfloor \frac{n}{d}\rfloor ⌊dn⌋互质的数之和
∑ i = 1 n i ∗ [ g c d ( i , n ) = 1 ] = { 1 n = 1 φ ( n ) n 2 n ≥ 2 \sum_{i=1}^ni*[gcd(i,n)=1]= \left\{\begin{array}{rcl} 1 && n=1\\ \frac{\varphi(n)n}{2} && n \geq 2\end{array}\right. ∑i=1ni∗[gcd(i,n)=1]={12φ(n)nn=1n≥2
证明:
- 当 n = 1 n=1 n=1时显然为 1 1 1
- 当 n ≥ 2 n\geq 2 n≥2时, φ ( n ) \varphi(n) φ(n)总为偶数,因为和 n n n互质的数总是成对出现,假设 n n n和 i i i互质,那么 n − i n-i n−i和 i i i也互质,他们的和为 n n n,并且有 φ ( n ) \varphi(n) φ(n)对,那么答案就是 φ ( n ) n 2 \frac{\varphi(n)n}{2} 2φ(n)n
那么最后的答案显然是 n ( n + ∑ d ≥ 2 , d ∣ n φ ( d ) d 2 ) n(n+\sum_{d \geq 2,d|n} \frac{\varphi(d)d}{2}) n(n+∑d≥2,d∣n2φ(d)d),这里的 d d d取遍 n n n的所有因数时实际上和 ⌊ n d ⌋ \lfloor \frac{n}{d}\rfloor ⌊dn⌋等价
考虑完上述性质,我们就可以得到类似埃氏筛预处理的代码了:
时间复杂度 O ( n l o g l o g n ) O(nloglogn) O(nloglogn)
//
// Created by Happig on 2020/9/28
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 1e6 + 10;
vector<int> prime;
bitset<maxn> vis;
ll phi[maxn], sum[maxn];
void init() {
phi[1] = 1;
for (int i = 2; i < maxn; i++) {
if (!vis[i]) {
prime.push_back(i);
phi[i] = i - 1;
}
for (int j = 0; j < prime.size() && i * prime[j] < maxn; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j]) {
phi[i * prime[j]] = phi[i] * phi[prime[j]];
} else {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
}
}
sum[1] = 1;
for (int i = 2; i < maxn; i++) {
sum[i]++;
for (int j = i; j < maxn; j += i) {
sum[j] += phi[i] * i / 2;
}
}
}
int main() {
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t, n;
init();
cin >> t;
while (t--) {
cin >> n;
cout << sum[n] * n << ENDL;
}
return 0;
}
线性筛再优化
显然上述代码不够优秀,因为根据狄利克雷卷积不难知道 f = i d ∗ φ f=id*\varphi f=id∗φ, f ( n ) = ∑ d ∣ n φ ( d ) d f(n)=\sum_{d|n} \varphi(d)d f(n)=∑d∣nφ(d)d是积性函数,那么肯定是可以线性筛的,这里参考了 O I − w i k i OI-wiki OI−wiki上的讲解,十分感谢!
-
首先考虑 f ( p ) , p ∈ p r i m e s f(p),p\in primes f(p),p∈primes,易得 f ( p ) = p ( p − 1 ) + 1 f(p)=p(p-1)+1 f(p)=p(p−1)+1
-
然后考虑 f ( p k ) f(p^k) f(pk),那么有 f ( p k ) = ∑ i = 0 k p i ∗ φ ( p i ) f(p^k)=\sum_{i=0}^k p^i*\varphi(p^i) f(pk)=∑i=0kpi∗φ(pi)
根据欧拉函数的性质:对于素数 p p p,若 n % i = = 0 n\%i==0 n%i==0,那么 φ ( p n ) = p ∗ φ ( n ) \varphi(pn)= p*\varphi(n) φ(pn)=p∗φ(n)
那么就有 f ( p k ) = ∑ i = 0 k p 2 k − 1 ( p − 1 ) f(p^k)=\sum_{i=0}^kp^{2k-1}(p-1) f(pk)=∑i=0kp2k−1(p−1)
同理得 f ( p k + 1 ) = f ( p k ) + p 2 k + 1 ( p − 1 ) f(p^{k+1})=f(p^k)+p^{2k+1}(p-1) f(pk+1)=f(pk)+p2k+1(p−1) -
对于线性筛中 p ∤ i p \nmid i p∤i的情况,显然为 f [ i ∗ p ] = f [ i ] ∗ f [ p ] f[i*p]=f[i] *f[p] f[i∗p]=f[i]∗f[p]
-
对于线性筛中 p ∣ i p | i p∣i的情况,令 i = x ∗ p c , g c d ( x , p c ) = 1 i=x*p^c,gcd(x,p^c)=1 i=x∗pc,gcd(x,pc)=1,可得 f [ i ∗ p ] = f [ x ] ∗ f [ p c + 1 ] ① f[i*p]=f[x]*f[p^{c+1}]① f[i∗p]=f[x]∗f[pc+1]①
然后又根据 f [ i ] = f [ x ] ∗ f [ p c ] ② f[i]=f[x]*f[p^c]② f[i]=f[x]∗f[pc]②, ① − ② ①-② ①−②有:
f [ i ∗ p ] − f [ i ] = f [ x ] ∗ p 2 c + 1 ( p − 1 ) ③ f[i*p]-f[i]=f[x]*p^{2c+1}(p-1)③ f[i∗p]−f[i]=f[x]∗p2c+1(p−1)③,那么同理可得:
f [ i ] − f [ i / p ] = f [ x ] ∗ p 2 c − 1 ( p − 1 ) ④ f[i]-f[i/p]=f[x]*p^{2c-1}(p-1)④ f[i]−f[i/p]=f[x]∗p2c−1(p−1)④,联立 ③ , ④ ③,④ ③,④最终得到:
f [ i ∗ p ] = f [ i ] + ( f [ i ] − f [ i / p ] ) ∗ p 2 f[i*p]=f[i]+(f[i]-f[i/p])*p^2 f[i∗p]=f[i]+(f[i]−f[i/p])∗p2
时间复杂度为 O ( n ) O(n) O(n)
//
// Created by Happig on 2020/9/28
//
#include <bits/stdc++.h>
#include <unordered_map>
#include <unordered_set>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define ins insert
#define Vector Point
#define ENDL "\n"
#define lowbit(x) (x&(-x))
#define mkp(x, y) make_pair(x,y)
#define mem(a, x) memset(a,x,sizeof a);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 1e6 + 10;
vector<int> prime;
bitset<maxn> vis;
ll f[maxn];
void init() {
f[1] = 1;
for (int i = 2; i < maxn; i++) {
if (!vis[i]) {
prime.push_back(i);
f[i] = 1LL * i * i - i + 1;
}
for (int j = 0; j < prime.size() && i * prime[j] < maxn; j++) {
vis[i * prime[j]] = 1;
if (i % prime[j]) {
f[i * prime[j]] = f[i] * f[prime[j]];
} else {
f[i * prime[j]] = f[i] + (f[i] - f[i / prime[j]]) * prime[j] * prime[j];
break;
}
}
}
}
int main() {
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t, n;
init();
cin >> t;
while (t--) {
cin >> n;
cout << (f[n] + 1) * n / 2 << ENDL;
}
return 0;
}