转载请注明出处,谢谢http://blog.csdn.net/bigtiao097?viewmode=contents
题意:
s(n)=∑i=1nφ(i)
φ(n) 是欧拉函数
给定n,求 s(n)mod109+7 (2≤n≤1010)
思路:
这是我学杜教筛做的第二个题,第一个就是和这个非常相似的求莫比乌斯函数之和
给大家推荐唐老师的一篇文章,个人感觉讲的非常好,我就是在这里学的
欧拉函数的一个性质是这样的
n=∑d|nφ(d)
所以就有
n×(n+1)2=∑i=1ni=∑i=1n∑d|iφ(d)=∑i=1n∑d=1⌊ni⌋φ(d)=∑i=1ns(⌊ni⌋)(1)(2)
这样就得到了
s(n)=n×(n+1)2−∑i=2ns(⌊ni⌋)
感觉上面式子中最难理解的地方就是
(1)→(2)
的过程了,这里稍微解释一下:
可以这样理解
∑i=1n∑d|iφ(d)=∑i=1n∑d|iφ(id)=∑i=1n∑j=1⌊ni⌋φ(j)
第二个式子中的 i 理解为枚举每一个数,最后一个式子中的 i 理解为枚举每一个因子
对于
s(n)=n×(n+1)2−∑i=2ns(⌊ni⌋)
然后就可以递归求解了,顺便加记忆化,顺便加上分段优化来求了
关于复杂度的问题,具体可以看上面推荐的文章,这里稍微说一下
- 如果我们先提前筛选出了 n23 的 φ(n) 的前缀和,总体复杂度大概是 O(n23)
- 如果没有提前处理一下,直接递归求解的话, 总体复杂度大概是 O(n34)
然后再说一下递归过程中记忆化的时候用的什么数据结构
可以看一下我的另一篇文章里面说的传送门
这个题用三种结构都能过,而且时间差不多
具体代码如下:
Result:Accepted
Memory:26108 KB
Time :1406 ms
#include<bits/stdc++.h>
const int maxn = 4641590;//maxn = n^(2/3)
const int mod = 1e9+7;
using namespace std;
typedef long long ll;
const int HASH_MOD=987654;
int phi[maxn];
bool vis[maxn];
int p[maxn];
ll n;
unordered_map<ll,ll> mp;
void euler_init()
{
phi[1]=1;
for(int i=2;i<maxn;i++)
{
if(!vis[i])
{
phi[i]=i-1;
p[++p[0]]=i;
}
for(int j=1;p[j]*i<maxn;j++)
{
vis[p[j]*i]=1;
if(i%p[j]==0)
{
phi[p[j]*i]=phi[i]*p[j]%mod;
break;
}
phi[p[j]*i]=phi[i]*(p[j]-1)%mod;
}
}
for(int i=1;i<maxn;i++)
phi[i]= (phi[i]+phi[i-1])%mod;
}
ll mod_mul(ll x,ll n)
{
ll res = 0;
while(n>0)
{
if(n&1) res = (res+x)%mod;
x = (x<<1)%mod;
n>>=1;
}
return res;
}
ll calc(ll x)
{
unordered_map<ll,ll>::iterator it;
if (x<maxn) return phi[x];
if((it=mp.find(x))!= mp.end())
return it->second;
ll ans = 0;
if(x&1)
ans = mod_mul(x,(x+1)/2);
else
ans = mod_mul(x/2,x+1);
for(ll l=2,r; l<=x; l=r+1)
{
r=x/(x/l);
ans=(ans-calc(x/l)*((r-l+1)%mod)%mod+mod)%mod;
}
return mp[x] = ans;
}
int main()
{
euler_init();
scanf("%lld",&n);
printf("%lld",calc(n));
}