链接:
https://www.51nod.com/Challenge/Problem.html#problemId=1239
题意:
对正整数n,欧拉函数是小于或等于n的数中与n互质的数的数目。此函数以其首名研究者欧拉命名,它又称为Euler's totient function、φ函数、欧拉商数等。例如:φ(8) = 4(Phi(8) = 4),因为1,3,5,7均和8互质。
S(n) = Phi(1) + Phi(2) + ...... Phi(n),给出n,求S(n),例如:n = 5,S(n) = 1 + 1 + 2 + 2 + 4 = 10,定义Phi(1) = 1。由于结果很大,输出Mod 1000000007的结果。
思路:
欧拉函数前缀和
对于欧拉函数首先有(n = sum_{d|n} varphi(d)), 所以可以得到(varphi(n) = n - sum_{d|n, d<n} varphi(d))
所以我们有
[sum_{i=1}^n varphi (i) = sum_{i=1}^n (i - sum_{d|i, d<i} varphi (d)) = sum_{i=1}^n i - sum_{i=2}^n sum_{d|i, d<i} varphi (d)
]
此时我们可以枚举(frac{i}{d})
[sum_{i=1}^n i - sum_{frac{i}{d} = 2}^n sum_{d=1}^{lfloor frac{n}{frac{i}{d}}
floor} varphi (d)
]
再把(frac{i}{d})看成(i),得到
[sum_{i=1}^n i - sum_{i=2}^n sum_{d=1}^{lfloor frac{n}{i}
floor} varphi (d)
]
得到
[sum(n) = sum_{i=1}^n i - sum_{i=2}^n sum(lfloor frac{n}{i}
floor)
]
使用记忆化搜索和hash存储的方式减少重复运算,分块处理每个(frac{n}{i})
代码:
//#include<bits/stdc++.h>
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<string.h>
#include<cstring>
#include<set>
#include<queue>
#include<algorithm>
#include<math.h>
#include<stdio.h>
#include<map>
#include<stack>
#include<list>
#define INF 0x3f3f3f3f
#define pii pair<int, int>
#define pll pair<LL, LL>
#define mp make_pair
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int MOD = 1e9+7;
const int HMOD = 999959;
const int VMOD = 5000000;
const int MAXN = 1010;
const int INV = 500000004;
const double eps = 1e-8;
int Next[4][2] = {-1, 0, 0, 1, 1, 0, 0, -1};
vector<pll> h[HMOD+5];
LL phi[VMOD+5];
bool isp[VMOD+5];
int pri[VMOD+5], cnt = 0;
LL n;
void init()
{
//欧拉函数打表
memset(isp, 0, sizeof(isp));
phi[1] = 1;
for (int i = 2;i <= VMOD;i++)
{
if (!isp[i])
{
pri[++cnt] = i;
phi[i] = i-1;
}
for (int j = 1;j <= cnt && i*pri[j] <= VMOD;j++)
{
isp[pri[j]*i] = 1;
if (i%pri[j] == 0)
{
phi[i*pri[j]] = phi[i]*pri[j]%MOD;
break;
}
phi[i*pri[j]] = phi[i]*(pri[j]-1)%MOD;
}
}
for (int i = 2;i <= VMOD;i++)
phi[i] = (phi[i-1]+phi[i])%MOD;
}
void upd(LL n, LL res)
{
h[n%HMOD].pb(mp(n, res));
}
LL cal(LL n)
{
if (n <= VMOD) return phi[n];
for (pll pa: h[n%HMOD]) if (pa.first == n)
return pa.second;
//记忆化搜索,hash记录
LL ans = 0;
for (LL l = 2;l <= n;)
{
LL x = n/l;
LL r = n/x;
ans = (ans + (r-l+1)%MOD*cal(x)%MOD)%MOD;
l = r+1;
}
ans = (((n%MOD*((n+1)%MOD)%MOD)*INV%MOD-ans)%MOD+MOD)%MOD;
upd(n, ans);
return ans;
}
int main()
{
IOS;
init();
scanf("%lld", &n);
printf("%lld", cal(n));
return 0;
}