分析:
题目要求:
在莫比乌斯反演介绍中,我提到过:phi函数完全可以用mu函数代替
那么这个式子我们也可以化成与mu函数有关的形式
式子很顺利的化出来了
仔细看了一下后半部分,我们欣喜的发现了一个熟悉的式子:
这个实际上就是
则:
(我这仔细一看,这不就是φ*1=n直接能化出来的吗,费了半天劲。。。)
我们考虑分块,
一旦分块之后,我们就面临着一个问题:求区间的phi值之和
这道题n的范围比较大,所以我们不能预处理了,只能上杜教筛
tip
最后两个点WA了
看了一下数据:
n=9956826265
ans=172447211
在缜密的排查之后,我发现在两个地方出现了问题:
一开始我这里是这样写的(WA):ll num=x%p*(x+1)%p*ni%p;
ll num=x%p*(x%p+1)%p*ni%p; //改成这样
在主程序中,一开始我写成了一个大式子(WA):
ans=(ans+((n/i)%p*((n/i)%p)*(sum(last)-sum(i-1))%p)%p)%p;
最后改成了这样:
ll k=(sum(last)-sum(i-1)+p)%p;
ll l=(n/i)%p;
ans=(ans+(l%p*l%p*k%p)%p)%p;
在之后的试验中,我又发现这样也是可以的:
ll k=(sum(last)-sum(i-1)+p)%p;
ans=(ans+( (n/i)%p*( (n/i)%p ) *k%p )%p)%p;
在预处理的时候,我处理了前1e7的数据
前几个点会慢一点,但是整体上时间会快一点
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
const ll p=1e9+7;
const ll ni=5e8+4;
const int N=1e7+5;
const int maxn=1e6+5;
bool no[N];
ll n,f[maxn],phi[N],h[maxn];
int tot=0,sshu[N];
void make()
{
phi[1]=1;
for (ll i=2;i<N;i++)
{
if (!no[i])
{
sshu[++tot]=i;
phi[i]=i-1;
}
for (ll 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 (ll i=2;i<N;i++) phi[i]=(phi[i]+phi[i-1])%p;
}
int hash(ll x)
{
ll t=x%maxn;
while (h[t]&&h[t]!=x) t=(t+1)%maxn;
return t;
}
ll sum(ll x)
{
if (x<N) return phi[x];
ll l=hash(x);
if (h[l]) return f[l];
ll num=x%p*(x%p+1)%p*ni%p; //
ll last;
for (ll i=2;i<=x;i=last+1)
{
last=x/(x/i);
num-=(ll)(last-i+1)*sum(x/i)%p;
}
num=(num%p+p)%p;
h[l]=x; f[l]=num;
return num;
}
int main()
{
make();
scanf("%lld",&n);
ll last,ans=0;
for (ll i=1;i<=n;i=last+1)
{
last=n/(n/i);
ll k=(sum(last)-sum(i-1)+p)%p; //
ll l=(n/i)%p;
ans=(ans+(l%p*l%p*k%p)%p)%p;
}
printf("%lld",ans);
return 0;
}