您的位置:首页 > 其它

51nod 1227 平均最小公倍数【欧拉函数+杜教筛】

2018-01-22 10:10 330 查看

以后这种题能用phi的就不要用mu…mu往往会带着个ln然后被卡常致死
把题目要求转换为前缀和相减的形式,写出来大概是要求这样一个式子:
\[ \sum_{i=1}^{n}\sum_{j=1}^{i}\frac{j}{gcd(i,d)} \]
注意j的限制是i
\[ \sum_{d=1}^{n}\sum_{i=1}^{n}\sum_{j=1}^{i}[gcd(i,j)==d]\frac{j}{d} \]
\[ \sum_{d=1}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{i}[gcd(i,j)==1]\frac{jd}{d} \]
\[ \sum_{d=1}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{i}[gcd(i,j)==1]j \]
然后有一个打表找规律发现的式子:
\[ \sum_{i=1}^{n}\sum_{j=1}^{i}[gcd(i,j)==1]i*j=\sum_{i=1}^{n}i*\frac{\phi(i)*i+[i==1]}{2} \]
于是原式可转化为:
\[ \sum_{d=1}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\frac{\phi(i)*i+[i==1]}{2} \]
\[ \frac{\sum_{d=1}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\phi(i)*i}{2}+\frac{n}{2} \]
先不考虑后面的加和下面的除二,于是要求的就是:
\[ \sum_{d=1}^{n}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\phi(i)*i \]
\( \sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\phi(i)*i \)的部分显然可以用杜教筛处理,然后拒绝算时间复杂度。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const long long N=1000005,m=1000000,inv6=166666668,inv2=500000004,mod=1e9+7;
long long a,b,n,phi
,q
,tot,ha
;
bool v
;
long long clc1(long long x)
{
return x*(x+1)%mod*inv2%mod;
}
long long clc2(long long x)
{
return x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
}
long long slv(long long x)
{
if(x<=m)
return phi[x];
if(ha[n/x])
return ha[n/x];
long long re=clc2(x);
for(long long i=2,la;i<=x;i=la+1)
{
la=x/(x/i);
re=(re-(clc1(la)-clc1(i-1))%mod*slv(x/i)%mod)%mod;
}
return ha[n/x]=re;
}
long long wk(long long x)
{
n=x;
memset(ha,0,sizeof(ha));
long long re=0ll;
for(long long i=1,la;i<=x;i=la+1)
{
la=x/(x/i);
re=(re+(la-i+1)*slv(x/i)%mod)%mod;
}
return (re+x)*inv2%mod;
}
int main()
{
phi[1]=1;
for(long long i=2;i<=m;i++)
{
if(!v[i])
{
q[++tot]=i;
phi[i]=i-1;
}
for(long long j=1;j<=tot&&i*q[j]<=m;j++)
{
long long k=i*q[j];
v[k]=1;
if(i%q[j]==0)
{
phi[k]=phi[i]*q[j];
break;
}
phi[k]=phi[i]*(q[j]-1);
}
}
for(long long i=1;i<=m;i++)
phi[i]=(phi[i-1]+phi[i]*i%mod)%mod;
scanf("%lld%lld",&a,&b);
printf("%lld\n",((wk(b)-wk(a-1))%mod+mod)%mod);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: