一道水题---lucas的数论

题目很简洁:

i=1nj=1nf(i×j)

其中 f(x) 表示 x 的约数个数.答案很大,mod 1000000007后输出.
数据范围:1n109

解法:

d|(i×j) 等价于 dgcd(i,d)|j
e=gcd(i,d), i=k1e, d=k2e
则只需且必须: k1|ik2|jgcd(k1,k2)=1
所以

ans=i=1nj=1nf(i×j)

=i=1nj=1nk1|ink2|jn[gcd(k1,k2)=1]

=k1=1nk2=1n[gcd(k1,k2)=1]k1|ink2|jn1

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

到了这一步,就用本人比较喜欢的方式进行莫比乌斯反演.
f(x)=k1=1nk2=1n[gcd(k1,k2)=x][nk1][nk2]

F(x)=k1=1nk2=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|k1nd|k2n[nk1][nk2]
=k1=1ndk2=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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值