一道水題---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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章