分析:
这道题看的是唐老师的blog
感觉还是一知半解,然而学校里的dalao都去长沙了
只留下我们这些蒟蒻(然而蒟蒻没有dalao的点拨怎么学习啊。。。)
用了将近一小时,终于理解了,尝试着来化简一下:
我们要求的是
在化简之前,先提出phi函数的一个性质:
我们就从这个式子下手:
得到的是一个类似于递推式的形式
我们预处理出O(sqrt(n))个F(n/i)的值,我们就可以递推出F(n)的值
因为phi函数是一个积性函数,F(n)是积性函数的前缀和,我们可以在线性筛的时候处理出一部分
假设预处理了前k个正整数的F(n),且k≥sqrt(n),
当k=O(n^2/3)时可以取到较好的复杂度:
具体的:
好像有两种实现方法:分块法 or 记忆化搜索
我在这里用的记忆化搜索,同时还要配合简单的hash:
int hash(ll x) //简单的hash
{
ll t=x%maxn;
while (h[t]&&h[t]!=x) t=(t+1)%maxn;
return t;
}
非常简陋的hash,但是竟然管用。。。
tip
空间卡的挺紧,只能开一个ll的数组
注意取%
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#define ll long long
using namespace std;
const int maxn=1e7+5;
const int N=1e6+5;
const ll p=1000000007;
const ll ni=500000004;
ll h[maxn],n;
int phi[N],tot=0,sshu[N],sum[N],f[maxn];
bool no[N];
void make()
{
phi[1]=1;
for (int i=2;i<N;i++)
{
if (!no[i])
{
sshu[++tot]=i;
phi[i]=i-1;
}
for (int j=1;j<=tot&&sshu[j]*i<N;j++)
{
no[sshu[j]*i]=1;
if (i%sshu[j]==0)
{
phi[i*sshu[j]]=phi[i]*sshu[j];
break;
}
phi[i*sshu[j]]=phi[i]*phi[sshu[j]];
}
}
for (int i=1;i<N;i++) sum[i]=(sum[i-1]+phi[i])%p;
}
int hash(ll x) //简单的hash
{
ll t=x%maxn;
while (h[t]&&h[t]!=x) t=(t+1)%maxn;
return t;
}
ll cal(ll n)
{
if (n<N) return sum[n];
ll x=(ll)((n%p)*((n+1)%p))%p*ni%p; //n(n+1)/2
ll last;
int l=hash(n);
if (h[l]) return f[l];
for (ll i=2;i<=n;i=last+1)
{
last=n/(n/i);
x=x-(last-i+1)*cal(n/i)%p;
}
x=(x%p+p)%p;
h[l]=n; f[l]=x;
return x;
}
int main()
{
make();
scanf("%lld",&n);
if (n<N)
{
printf("%lld",(ll)sum[n]);
return 0;
}
printf("%lld\n",cal(n));
return 0;
}