51nod1227-平均最小公倍数【杜教筛,欧拉函数】
正题
题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=1227
题目大意
定义
\[F(a)=\frac{\sum_{i=1}^a lcm(a,i)}{a}
\]
给出\(l,r\)求\(\sum_{i=l}^rF(i)\)
解题思路
好久没做数论题了
直接拆成两个前缀和的差,然后有
\[\sum_{i=1}^n\sum_{j=1}^i\frac{j}{gcd(i,j)}
\]
\[\sum_{d=1}^n\frac{1}{d}\sum_{i=1}^n\sum_{j=1}^ij[gcd(i,j)=d]
\]
\[\sum_{d=1}^n\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j=1}^{i}j[gcd(i,j)=1]
\]
\[\frac{1}{2}+\sum_{d=1}^n\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}\frac{\varphi(i)i}{2}
\]
函数\(H(n)=\varphi(n)n\)可以用杜教筛,\(H\times id\)就可以得到\(n^2\)的函数。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define ll long long
using namespace std;
const ll N=5e6,P=1e9+7;
ll cnt,pri[N/10],phi[N];
bool v[N];map<ll,ll> mp;
void Prime(){
phi[1]=1;
for(ll i=2;i<N;i++){
if(!v[i])pri[++cnt]=i,phi[i]=i-1;
for(ll j=1;j<=cnt&&i*pri[j]<N;j++){
v[i*pri[j]]=1;
if(i%pri[j]==0){
phi[i*pri[j]]=phi[i]*pri[j];
break;
}
phi[i*pri[j]]=phi[i]*(pri[j]-1);
}
}
for(ll i=1;i<N;i++)
phi[i]=(phi[i]*i+phi[i-1])%P;
return;
}
ll GetS(ll n)
{return n*(n+1)/2%P;}
ll GetSS(ll n)
{return n*(n+1)%P*(2*n+1)%P*((P+1)/6)%P;}
ll GetPhi(ll n){
if(n<N)return phi[n];
if(mp[n])return mp[n];
ll ans=GetSS(n);
for(ll l=2,r;l<=n;l=r+1){
r=n/(n/l);
(ans-=GetPhi(n/l)*(GetS(r)-GetS(l-1))%P)%=P;
}
mp[n]=ans;
return ans;
}
ll solve(ll n){
ll ans=0;
if(!n)return 0;
for(ll l=1,r;l<=n;l=r+1){
r=n/(n/l);
(ans+=(GetPhi(n/l)+1)*(r-l+1)%P)%=P;
}
return ans*((P+1)/2)%P;
}
signed main()
{
ll l,r,ans;Prime();
scanf("%lld%lld",&l,&r);
ans=(solve(r)-solve(l-1))%P;
printf("%lld\n",(ans+P)%P);
return 0;
}