1239 欧拉函数之和
欧拉函数 φ ( n ) \varphi(n) φ(n)表示小于等于 n n n的与 n n n互质的数的个数。
令答案 f ( n ) = ∑ i = 1 n φ ( i ) f(n)=\sum\limits_{i=1}^{n}\varphi(i) f(n)=i=1∑nφ(i)。
可以证明 n = ∑ d ∣ n φ ( d ) n=\sum\limits_{d|n}\varphi(d) n=d∣n∑φ(d)。
下面是一个我理解的很不正经的证明:
列出以 n n n为分母的所有分数: 1 n , 2 n , . . . , n n \dfrac{1}{n},\dfrac{2}{n},...,\dfrac{n}{n} n1,n2,...,nn。
可以理解 φ ( b ) \varphi(b) φ(b)表示的是以 b b b为分母的最简真分数的个数。
a b ( a < b ) \dfrac{a}{b}(aba(a<b)为最简分数,当且仅当 b ∣ n b|n b∣n且 ( a , b ) = 1 (a,b)=1 (a,b)=1。
你仔细地想, ∑ d ∣ n φ ( d ) \sum\limits_{d|n}\varphi(d) d∣n∑φ(d)就表示出了 1 n , 2 n , . . . , n n \dfrac{1}{n},\dfrac{2}{n},...,\dfrac{n}{n} n1,n2,...,nn,于是 n = ∑ d ∣ n φ ( d ) n=\sum\limits_{d|n}\varphi(d) n=d∣n∑φ(d)。
∴ n = φ ( n ) + ∑ d ∣ n , d < n φ ( d ) \therefore n=\varphi(n)+\sum\limits_{d|n,d
∴ φ ( n ) = n − ∑ d ∣ n , d < n φ ( d ) \therefore\varphi(n)=n-\sum\limits_{d|n,d
∴ f ( n ) = ∑ i = 1 n ( i − ∑ d ∣ i , d < i φ ( d ) ) \therefore f(n)=\sum\limits_{i=1}^{n}\left(i-\sum\limits_{d|i,d∴f(n)=i=1∑n⎝⎛i−d∣i,d<i∑φ(d)⎠⎞
∴ f ( n ) = ∑ i = 1 n i − ∑ i = 1 n ∑ d ∣ i , d < i φ ( d ) = n ( n + 1 ) 2 − ∑ i = 1 n ∑ d ∣ i , d < i φ ( d ) \therefore f(n)=\sum\limits_{i=1}^{n}i-\sum\limits_{i=1}^{n}\sum\limits_{d|i,d∴f(n)=i=1∑ni−i=1∑nd∣i,d<i∑φ(d)=2n(n+1)−i=1∑nd∣i,d<i∑φ(d)
∑ i = 1 n ∑ d ∣ i , d < i φ ( d ) \sum\limits_{i=1}^{n}\sum\limits_{d|i,di=1∑nd∣i,d<i∑φ(d),算贡献,则可以等价为: ∑ d = 1 n ∑ i = 1 ⌊ n d ⌋ φ ( d ) = ∑ d = 1 n ( ⌊ n d ⌋ ⋅ φ ( d ) ) \sum\limits_{d=1}^{n}\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\varphi(d)=\sum\limits_{d=1}^{n}\left(\lfloor\frac{n}{d}\rfloor·\varphi(d)\right) d=1∑ni=1∑⌊dn⌋φ(d)=d=1∑n(⌊dn⌋⋅φ(d)),下面这个图可以帮助理解,但我无法言说:
(其中 k = ⌊ n d ⌋ k=\lfloor\frac{n}{d}\rfloor k=⌊dn⌋)
但是这样还是不能递推,那就换一种方法,枚举 d d d的系数 i i i,再枚举 d d d并统计 i d id id之前(由此可以得到枚举的 d d d必须满足 d ≤ ⌊ n i ⌋ d\leq\lfloor\frac{n}{i}\rfloor d≤⌊in⌋,因为 i d ≤ n id\leq n id≤n)的 d d d: ∑ i = 1 n ∑ d = 1 ⌊ n i ⌋ φ ( d ) \sum\limits_{i=1}^{n}\sum\limits_{d=1}^{\lfloor\frac{n}{i}\rfloor}\varphi(d) i=1∑nd=1∑⌊in⌋φ(d)
于是递推式有了: f ( n ) = n ( n + 1 ) 2 − ∑ i = 1 n f ( ⌊ n i ⌋ ) f(n)=\dfrac{n(n+1)}{2}-\sum\limits_{i=1}^{n}f(\lfloor\frac{n}{i}\rfloor) f(n)=2n(n+1)−i=1∑nf(⌊in⌋)
直接记忆化搜索还是会超时,所以我们需要用分块(俗称数线段)。
发现对于某一区域 [ l , r ] [l,r] [l,r]内 i i i的 f ( ⌊ n i ⌋ ) f(\lfloor\frac{n}{i}\rfloor) f(⌊in⌋)是相等的:
( n = 10 n=10 n=10)
i i i | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
⌊ n i ⌋ \left\lfloor\dfrac{n}{i}\right\rfloor ⌊in⌋ | 10 | 5 | 3 | 2 | 2 | 1 | 1 | 1 | 1 | 1 |
当 i ∈ [ 6 , 10 ] i\in[6,10] i∈[6,10]时,所有的 f ( ⌊ n i ⌋ ) f(\lfloor\frac{n}{i}\rfloor) f(⌊in⌋)都是 1 1 1,于是 ∑ i = 6 10 f ( ⌊ n i ⌋ ) = ( 10 − 6 + 1 ) × f ( ⌊ n 6 ⌋ ) = 5 ⋅ f ( 1 ) \sum\limits_{i=6}^{10}f(\lfloor\frac{n}{i}\rfloor)=(10-6+1)\times f(\lfloor\frac{n}{6}\rfloor)=5\cdot f(1) i=6∑10f(⌊in⌋)=(10−6+1)×f(⌊6n⌋)=5⋅f(1),节省了 4 4 4次计算。
以下内容我不负责,因为我没有试过。
可能你会说,由于记忆化了,所以重复调用也没关系。
但是我的记忆化用的map
,所以有 log x \log x logx的复杂度。
如果你可以用unordered_map
或者你愿意手打vector
的哈希,有可能不会超时。
继续。
找规律发现当 i ∈ [ l , ⌊ n ⌊ n l ⌋ ⌋ ] i\in \left[l,\lfloor\frac{n}{\lfloor\frac{n}{l}\rfloor}\rfloor\right] i∈[l,⌊⌊ln⌋n⌋]时,所有 f ( ⌊ n i ⌋ ) = f ( ⌊ n l ⌋ ) f(\lfloor\frac{n}{i}\rfloor)=f(\lfloor\frac{n}{l}\rfloor) f(⌊in⌋)=f(⌊ln⌋),于是做完了。
直接分块记忆化搜索还是会超时,所以要把 φ ( n ) , n ≤ 5 × 1 0 6 \varphi(n),n\leq 5\times10^6 φ(n),n≤5×106线性筛筛出来并计算前缀和(打 n ≤ 5 × 1 0 6 n\leq 5\times10^6 n≤5×106时的表)。
将 n n n唯一分解: n = p 1 α 1 p 2 α 2 . . . p m α m n={p_1}^{\alpha_1}{p_2}^{\alpha_2}...{p_m}^{\alpha_m} n=p1α1p2α2...pmαm
则: φ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) . . . ( 1 − 1 p m ) = n ⋅ p 1 − 1 p 1 ⋅ p 2 − 1 p 2 ⋅ . . . ⋅ p m − 1 p m \varphi(n)=n(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_m})=n\cdot\frac{p_1-1}{p_1}\cdot\frac{p_2-1}{p_2}\cdot...\cdot\frac{p_m-1}{p_m} φ(n)=n(1−p11)(1−p21)...(1−pm1)=n⋅p1p1−1⋅p2p2−1⋅...⋅pmpm−1
不正经的证明(信竞要的是玄学感觉):
根据容斥原理。
我们需要把小于 n n n的 p i ( i ≤ k ) p_i(i\leq k) pi(i≤k)的倍数个数全部减去,
(会减重,如 p 1 p 2 p_1p_2 p1p2这个数被减了 2 2 2次)再把小于 n n n的 p i p j ( i ≤ m , j ≤ m , i ≠ j ) p_ip_j(i\leq m,j\leq m,i\neq j) pipj(i≤m,j≤m,i=j)的倍数全部加上,
(会加多)再把小于 n n n的 p i p j p k ( i ≤ m , j ≤ m , k ≤ m , i ≠ j ≠ k ) p_ip_jp_k(i\leq m,j\leq m,k\leq m,i\neq j\neq k) pipjpk(i≤m,j≤m,k≤m,i=j=k)的倍数全部减去,
……
小于 n n n的 k k k的倍数的个数是 ⌊ n k ⌋ \lfloor\frac{n}{k}\rfloor ⌊kn⌋,由于 p i pi pi能整除 n n n,所以 n ∏ p x \dfrac{n}{\prod p_x} ∏pxn一定是整数。
把式子中的 n n n乘进去: φ ( n ) = ( n − n p 1 ) ( 1 − 1 p 2 ) . . . ( 1 − 1 p m ) \varphi(n)=(n-\frac{n}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_m}) φ(n)=(n−p1n)(1−p21)...(1−pm1)
想象一下把它全部展开,就是一个容斥。
φ ( n ) = n ⋅ p 1 − 1 p 1 ⋅ p 2 − 1 p 2 ⋅ . . . ⋅ p m − 1 p m \varphi(n)=n\cdot\frac{p_1-1}{p_1}\cdot\frac{p_2-1}{p_2}\cdot...\cdot\frac{p_m-1}{p_m} φ(n)=n⋅p1p1−1⋅p2p2−1⋅...⋅pmpm−1
首先phi[1]=1
。
枚举i>1
,若此时的phi[i]
还没有值,说明i
是素数(往下看就知道为什么了)。
用筛素数的原理,当i
是素数时,它的倍数都不是素数。
再枚举i
的倍数j
,更新phi[j]
(所以phi
充当了筛素数时的isPrime
数组)。
由于i
肯定是j
的一个质因子,所以phi[j]*=(i-1)/i
,当然,如果此时是phi[j]
第一次被更新,应该先把phi[j]
赋为j
。
#include
#include
#include
using namespace std;
#define LL long long
#define MAXT 5000000
#define MOD 1000000007
#define INV2 500000004
LL phiSum[MAXT+5];
map<LL,LL> M;
void GetPhi(int N){
phiSum[1]=1;
for(int i=2;i<=N;i++){
if(!phiSum[i]){
for(int j=i;j<=N;j+=i){
if(!phiSum[j])
phiSum[j]=j;
phiSum[j]=phiSum[j]/i*(i-1);
}
}
}
for(int i=1;i<=N;i++)
phiSum[i]=(phiSum[i]+phiSum[i-1])%MOD;//计算前缀和
}
LL Solve(LL x){
if(x<=MAXT)//打了表就直接返回
return phiSum[x];
if(M.count(x))//记忆化
return M[x];
LL ret=x%MOD*((x+1)%MOD)%MOD*INV2%MOD;
for(LL l=2,r;l<=x;l=r+1)//数线段
r=x/(x/l),ret=((ret-(r-l+1)%MOD*Solve(x/l)%MOD)%MOD+MOD)%MOD;
return M[x]=ret;
}
int main(){
GetPhi(MAXT);
LL N;
scanf("%lld",&N);
printf("%lld",Solve(N));
}