[51nod1238]最小公倍数之和

题目大意

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

题解

太懒了
这里写的很好

#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=10000000+10,maxha=10000000,mo=1000000007,inv2=(mo+1)/2,inv6=(mo+1)/6;
int phi[maxn],pri[maxn],g[maxn];
bool bz[maxn];
int gg[maxn];
ll k,l,t,top,ans;
ll i,j,n,m;
void prepare(){
    ll i,j;
    phi[1]=1;
    fo(i,2,maxn-10){
        if (!bz[i]) pri[++top]=i,phi[i]=i-1;
        fo(j,1,top){
            if ((ll)i*pri[j]>maxn-10) break;
            bz[i*pri[j]]=1;
            if (i%pri[j]==0){
                phi[i*pri[j]]=phi[i]*pri[j];
                break;
            }
            phi[i*pri[j]]=phi[i]*(pri[j]-1);
        }
    }
    fo(i,1,maxn-10) g[i]=(g[i-1]+(ll)phi[i]*i%mo*i%mo)%mo;
}
ll getsum(ll r){
    r%=mo;
    return (ll)r*(r+1)%mo*((2*r+1)%mo)%mo*inv6%mo;
}
ll calcg(ll n){
    if (n<=maxn-10) return g[n];
    ll i,j,k;
    ll t;
    ll l=m/n,r=n%mo;
    if (gg[l]) return gg[l];
    t=(ll)r*(r+1)%mo*inv2%mo;
    t=(ll)t*t%mo;
    i=2;
    while (i<=n){
        j=n/(n/i);
        (t-=(ll)(getsum(j)-getsum(i-1))*calcg(n/i)%mo)%=mo;
        (t+=mo)%=mo;
        i=j+1;
    }
    return gg[l]=t;
}
int main(){
    prepare();
    i=1;
    scanf("%lld",&n);
    m=n;
    while (i<=n){
        j=n/(n/i);
        ans=(ans+(ll)((i+j)%mo)*(j-i+1)%mo*inv2%mo*calcg(n/i)%mo)%mo;
        i=j+1;
    }
    (ans+=mo)%=mo;
    printf("%d\n",ans);
}

你可能感兴趣的:(筛法)