51nod 1237 最大公约数之和 V3

51nod 1237 最大公约数之和 V3

原题链接:
https://www.51nod.com/onlineJudge/submitDetail.html#!judgeId=353365

题面错误。原意是计算:
G=i=1nj=1ngcd(i,j)

G=i=1nj=1ngcd(i,j)=d=1ndi=1nj=1n[gcd(i,j)=d]

应用经典反演有:
f(d)=i=1nj=1n[gcd(i,j)=d]F(d)=d|af(a)=d|i,ind|j,jn1=nd2f(d)=d|aμ(ad)F(a)

方法1(复杂度较高)

此时不在化简。之间对
d=1ndf(d)
计算。

f(d)=A(nd)

这是因为:
f(d)=d|aμ(ad)F(a)=a=1ndμ(a)nda2

记, A 的定义为:
A(n)=a=1nμ(a)na2

A 分段计算即可。需要计算M(梅藤斯函数)

M(n)=i=1nμ(i)

因为: μI=e

所以:
M(n)=1i=2nM(ni)

方法2

G=d=1ndd|aμ(ad)F(a)=d=1ndd|af(d)=d|aμ(ad)na2=a=1nna2d|aμ(ad)d=a=1nna2φ(a)

其中:
φI=N

所以:
Sφ(n)=n(n+1)2i=2nSφ(ni)

给出第二种方法的代码:

#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <cmath>
#define MAXN 2111111
#define SQR 111111

using namespace std;
typedef long long LL;
const int P= 1e9+7;
const int IV= P-P/2;

int phi[MAXN];
int Sp[MAXN];
int _Sp[SQR];


void init()
{
    for(int i=1,j;i<MAXN;Sp[i]=(Sp[i-1]+phi[i])%P,i++)
        for(j=i<<1,phi[i]=i-phi[i];j<MAXN;j+=i)phi[j]+=phi[i];
}

int S1(LL n)
{
    n%=P;
    return n*(n+1)%P*IV%P;
}

void clat_P(LL n,int d)
{
    int m=sqrt(n)+1,ans=0;
    for(int L=1;L<m;L++)
    {
        LL u=(n/L-n/(L+1))%P;
        ans+=u*Sp[L]%P;
        if(ans>=P)ans-=P;
    }
    for(int i=(int)(n/m);i>1;i--)
    {
        LL u=n/i;
        if(u<MAXN)
            u=Sp[u];
        else
            u=_Sp[i*d];
        ans+=u;
        if(ans>=P)ans-=P;
    }
    _Sp[d]=(S1(n)-ans+P)%P;
}

void slove_P(LL n)
{
    if(n<MAXN)return;
    for(int i=(int)(n/MAXN);i;i--)clat_P(n/i,i);
}
int slove(LL n)
{
    slove_P(n);
    int m=sqrt(n)+1,ans=0;
    for(int L=1;L<m;L++)
    {
        LL f1=n/L,f2=n/(L+1);
        if(f1<MAXN)
            f1=Sp[f1];
        else
            f1=_Sp[L];
        if(f2<MAXN)
            f2=Sp[f2];
        else
            f2=_Sp[L+1];
        LL u=L;
        u=u*u%P;
        ans+=(f1-f2+P)*u%P;
        if(ans>=P)ans-=P;
    }
    for(int i=(int)(n/m);i;i--)
    {
        LL u=n/i;
        u%=P;
        u=u*u%P;
        ans+=u*phi[i]%P;
        if(ans>=P)ans-=P;
    }
    return ans;
}
int main ()
{
    init();
    LL n;
    scanf("%lld",&n);
    printf("%d\n",slove(n));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值