[日常摸鱼]51nod1237-最大公约数之和V3-杜教筛

意:求$\sum_{i=1}^n \sum_{j=1}^n gcd(i,j),n<=1e10$

 


 

 

之前刚好在UVA上也做过一个这样求和的题目,不过那个数据范围比较小,一开始用类似的方法

$ans=\sum_{i=1}^n \sum_{j=1}^i gcd(i,j)-\sum_{i=1}^n i$

先考虑化简$\sum_{i=1}^n gcd(i,n)$变成好求和的形式

$$\begin{aligned} \sum_{i=1}^n gcd(i,n) &=\sum_{i=1}^n \sum_{d=1}^n d*[gcd(i,n)=d]\\ &=\sum_{d=1}^n d \sum_{i=1}^n [gcd(i,n)=d] \\ &=\sum_{d=1}^n d \sum_{\frac{i}{d}=1}^{\frac{n}{d}} [gcd(\frac{i}{d},\frac{n}{d})=1]\\ &=\sum_{d|n} d*\phi(\frac{n}{d}) \end{aligned}$$

发现是$f(n)=n$和$g(n)=\phi(n)$的卷积,令$S(n)$表示$g$的前缀和,然后非常套路地,然后刚刚那一坨的前缀和就变成求$\sum_{i=1}^n i*S(\lfloor \frac{n}{i} \rfloor)$,欧拉函数前缀和直接用杜教筛算,然后分块求和

 

一开始取模一直写挂…orz

(有点懒直接用map存了)

#include<cstdio>
#include<cstring>
#include<map>
using namespace std;

typedef long long lint;

const lint MOD=1000000007;
const lint N=4000005;
const lint G=100005;

lint n,tot,inv2;
lint pri[N],phi[N];
bool p[N];
map<lint,lint>mp;

inline void init()
{
    p[1]=1;phi[1]=1;
    for(register lint i=2;i<N;i++)
    {
        if(!p[i])
        {
            pri[++tot]=i;
            phi[i]=i-1;
        }
        for(register lint j=1;j<=tot&&i*pri[j]<N;j++)
        {
            lint t=i*pri[j];p[t]=1;
            if(i%pri[j]==0){phi[t]=phi[i]*pri[j];break;}
            phi[t]=phi[i]*(pri[j]-1);
        }
    }
    for(register lint i=1;i<N;i++)phi[i]=(phi[i]+phi[i-1])%MOD;
}

inline lint pow_mod(lint a,lint b,lint p)
{
    lint res=1;
    for(;b;b>>=1,a=(a*a)%p)if(b&1)res=(res*a)%p;
    return res%p;
}

inline lint sum(lint x)
{
    return x%MOD*((x+1)%MOD)%MOD*inv2%MOD;
}

inline lint calc_phi(lint x)
{
    if(x<N)return phi[x];
    if(mp.count(x))return mp[x];
    lint res=sum(x),pos;
    for(register lint i=2;i<=x;i=pos+1)
    {
        pos=x/(x/i);
        res-=((pos-i+1)%MOD*calc_phi(x/i)%MOD)%MOD;
        res=(res%MOD+MOD)%MOD;
    }return mp[x]=res;
}

inline lint calc_ans(lint x)
{
    lint res=0,pos;
    for(register lint i=1;i<=x;i=pos+1)
    {
        pos=x/(x/i);
        res+=(sum(pos)-sum(i-1))%MOD*calc_phi(x/i)%MOD;
        res=(res%MOD+MOD)%MOD;
    }return res;
}

int main()
{
    init();inv2=pow_mod(2,MOD-2,MOD);
    scanf("%lld",&n);lint ans=calc_ans(n)%MOD;
    ans=(ans*2)%MOD-sum(n);ans=(ans%MOD+MOD)%MOD;
    printf("%lld",ans);
    return 0;
}
View Code

 

posted @ 2017-12-13 20:06  yoshinow2001  阅读(259)  评论(0编辑  收藏  举报