【51nod1363】最小公倍数之和

来源:互联网 发布:云计算工程师就业 编辑:程序博客网 时间:2024/05/22 08:14

Description

给出一个n,求1-n这n个数,同n的最小公倍数的和。
例如:n = 6,1,2,3,4,5,6 同6的最小公倍数分别为6,6,6,12,30,6,加在一起 = 66。
由于结果很大,输出Mod 1000000007的结果。

Solution

这道题化简到一半,差点废了,后来经高人指点才明白……

ans=i=1ningcd(i,n)=nd|ni=1n/di[gcd(i,n/d)==1]

我们发现当存在i与x互质时, x-i与x也是对称的,换言之,与x对称的数是对称存在的,两两相加和为x,除了1。
ans=n+nd|nϕ(n/d)n/d2=n+n2d|n,d>1ϕ(d)d
我们把n的每个质因子提出来,算出它对答案的贡献,然后乘起来就可以。形如(a+b)*(c+d)=ac+ad+bc+bd
n=i=1Bpiai

d|nϕ(d)d=i=1Bj=0aiϕ(pij)pij=i=1B1+j=1ai(pi1)pi2j1=i=1B1+(pi1)pi2ai+1pipi21=i=1B1+pi2ai+1pipi+1
最后只要把d=1的状态剪去即可,即减1。
T=i=1B1+pi2ai+1pipi+1

ans=n+n2(T1)

Code

#include<iostream>#include<cmath>#include<cstring>#include<cstdio>#include<algorithm>#define ll long longusing namespace std;const ll maxn=1e5;const ll mo=1e9+7,mo2=5e8+4;ll d[maxn],bz[maxn+5];ll n,m,i,t,j,k,l,x,y,z,ans;ll mi(ll x,ll y){    if (y==1) return x;    ll t=mi(x,y/2);    if (y%2==0) return t*t%mo;return t*t%mo*x%mo;}int main(){//  freopen("data.in","r",stdin);    scanf("%lld",&n);    for (i=2;i<=maxn;i++){        if (!bz[i]) d[++d[0]]=i;        for (j=1;j<=d[0];j++){            if (i*d[j]>maxn) break;            bz[i*d[j]]=1;            if (i%d[j]==0)break;        }    }    for (;n;n--){        scanf("%lld",&x);ans=1;y=x;        for (i=1;d[i]*d[i]<=x;i++){            if (x%d[i]) continue;            t=0;k=1;            while (x%d[i]==0) x/=d[i],t++;            k+=d[i]*(mi(d[i],2*t)-1)%mo*mi(d[i]+1,mo-2)%mo;            ans=ans*k%mo;        }        k=(1+(x-1)*x)%mo;        ans=ans*k%mo;        ans--;        ans=(ans*y%mo*mo2%mo+y)%mo;        printf("%lld\n",ans);    }}
1 0
原创粉丝点击