您的位置:首页 > 其它

一道水题---lucas的数论

2017-03-19 22:04 316 查看

题目很简洁:

求∑i=1n∑j=1nf(i×j)

其中f(x)表示x 的约数个数.答案很大,mod 1000000007后输出.

数据范围:1≤n≤109

解法:

d|(i×j) 等价于dgcd(i,d)|j

设e=gcd(i,d), i=k1⋅e, d=k2⋅e

则只需且必须:k1|i且k2|j,gcd(k1,k2)=1

所以

ans=∑i=1n∑j=1nf(i×j)

=∑i=1n∑j=1n∑k1|in∑k2|jn[gcd(k1,k2)=1]

=∑k1=1n∑k2=1n[gcd(k1,k2)=1]∑k1|in∑k2|jn1

=∑k1=1n∑k2=1n[gcd(k1,k2)=1]⋅[nk1]⋅[nk2]

到了这一步,就用本人比较喜欢的方式进行莫比乌斯反演.

令f(x)=∑k1=1n∑k2=1n[gcd(k1,k2)=x]⋅[nk1]⋅[nk2]

F(x)=∑k1=1n∑k2=1n[x|gcd(k1,k2)]⋅[nk1]⋅[nk2]

则由f,F的含义可得F[i]=∑i|dnf(d)

反演得

f(i)=∑i|dnμ(di)⋅F(d)特别地f(1)=∑d=1nμ(d)⋅F(d)

又F(d)=∑d|k1n∑d|k2n⋅[nk1]⋅[nk2] =∑k1=1nd∑k2=1nd[nk1d]⋅[nk2d]=(∑k=1nd[nkd])2

所以ans=f(1)=∑d=1nμ(d)⋅(∑k=1nd[nkd])2

考虑到nd的不同取值只有不到2n√种,故外层可以在O(n√)内枚举,内层同样的道理,也可以快速处理,复杂度不好分析,大约O(n34).

至于μ(d)怎么办,用杜教筛求快速前缀和即可,杜教筛复杂度O(n23).

参考代码

#include<cstdio>
#include<map>
using namespace std;
#define MAXN 3000010
#define mod 1000000007
#define LL long long
int lim=1000000;
int n,pri[MAXN],tot,mu[MAXN*10];
bool vis[MAXN*10];
void init()
{
mu[1]=1;
for(int i=2;i<=lim;i++)
{
if(!vis[i])
{
pri[++tot]=i;
mu[i]=-1;
}
for(int j=1;j<=tot&&i*pri[j]<=lim;j++)
{
vis[i*pri[j]]=1;
if(i%pri[j]==0)
{
mu[i*pri[j]]=0;
break;
}
else
{
mu[i*pri[j]]=-mu[i];
}
}
}
for(int i=1;i<=lim;i++)
{
mu[i]+=mu[i-1];
}
return ;
}
map<LL,LL>MU;
LL f(LL x)
{
if(x<=lim)
{
return mu[x];
}
if(MU.count(x))
{
return MU[x];
}
LL ans=0,e,ee;
for(e=2;e<=x;e=ee+1)
{
ee=(x/(x/e));
ans+=f(x/e)*(ee-e+1);
}
return MU[x]=1-ans;
}
int ans=0;
inline int cal(int x)
{
int ff,fff,an=0;
for(ff=1;ff<=x;ff=fff+1)
{
fff=x/(x/ff);
an+=1ll*(fff-ff+1)*(x/ff)%mod;
an-=an>=mod?mod:0;
}
return an;
}
int main()
{
scanf("%d",&n);
init();
int e,ee,q;
for(e=1;e<=n;e=ee+1)
{
ee=n/(n/e);
q=cal(n/e);
ans+=1ll*q*q%mod*(f(ee)-f(e-1))%mod;
ans+=ans<0?mod:0;
ans-=ans>=mod?mod:0;
}
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息