51nod1237 最大公约数之和 V3

9 篇文章 0 订阅

Description


给出一个数N,输出小于等于N的所有数,两两之间的最大公约数之和。

相当于计算这段程序(程序中的gcd(i,j)表示i与j的最大公约数):
由于结果很大,输出Mod 1000000007的结果。

G=0;
for(i=1;i

Solution


感觉有点不太对劲啊,为什么我推的柿子都和别人不太一样
先套路一波设一下

g(n)=i=1nj=1igcd(i,j)=i=1nj|ijφ(ij)=j=1nji=1niφ(i) g ( n ) = ∑ i = 1 n ∑ j = 1 i g c d ( i , j ) = ∑ i = 1 n ∑ j | i j ∗ φ ( i j ) = ∑ j = 1 n j ∑ i = 1 ⌊ n i ⌋ φ ( i )

那么答案有
ans=g(n)2i=1ni a n s = g ( n ) ∗ 2 − ∑ i = 1 n i

这里直接杜教筛筛出欧拉函数的前缀和,上分块搞
注意爆longlong的问题,直接开ull无济于事,气急败坏之下就一步步做

Code


#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)

typedef long long LL;

const LL MOD=1000000007;
const int HASH=1299709;
const int N=21544346;
const LL ny2=500000004;

int prime[1362202],phi[N+1];
bool not_prime[N+1];
LL h[HASH+2],f[HASH+2];
bool found;

LL hash(LL x) {
    found=false;
    LL y=x%HASH;
    while (h[y]&&h[y]!=x) {
        y=y+1;
        if (y>=HASH) y-=HASH;
    }
    if (h[y]==x) found=true;
    h[y]=x;
    return y;
}

void pre_work() {
    phi[1]=1;
    rep(i,2,N) {
        if (!not_prime[i]) {
            prime[++prime[0]]=i;
            phi[i]=i-1;
        }
        for (int j=1;j<=prime[0]&&i*prime[j]<=N;j++) {
            not_prime[i*prime[j]]=1;
            if (i%prime[j]==0) {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
        phi[i]=(phi[i-1]+phi[i])%MOD;
    }
}

inline LL get_phi(LL n) {
    if (n<=N) return phi[n];
    LL w=hash(n);
    if (found) return f[w];
    LL ret=0;
    for (LL i=2,j;i<=n;i=j+1) {
        j=n/(n/i);
        LL tmp=(j-i+1)%MOD;
        tmp=tmp*get_phi(n/i)%MOD;
        ret=(ret-MOD+tmp)%MOD;
    }
    n=n%MOD;
    LL tmp=(n+1)%MOD;
    tmp=tmp*n%MOD;
    tmp=tmp*ny2%MOD;
    ret=(tmp-ret+MOD)%MOD;
    f[w]=ret;
    return ret;
}

void solve(LL n) {
    // printf("%lld\n", get_phi(n));
    LL ans=0;
    for (LL i=1,j;i<=n;i=j+1) {
        j=n/(n/i);
        LL tmp=(i+j)%MOD;
        tmp=tmp*(j-i+1)%MOD;
        tmp=tmp*ny2%MOD;
        tmp=tmp*get_phi(n/i)%MOD;
        ans=(ans-MOD+tmp)%MOD;
    }
    n=n%MOD;
    ans=ans*2%MOD;
    LL tmp=(n+1)%MOD;
    tmp=tmp*n%MOD;
    tmp=tmp*ny2%MOD;
    ans=(ans+MOD-tmp+MOD)%MOD;
    printf("%lld\n", ans);
}

int main(void) {
    pre_work();
    LL n; scanf("%lld",&n);
    solve(n);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值