ccz181078

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

Description

今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数a和b,LCM(a, b)表示能同时被a和b整除的最小正整数。例如,LCM(6, 8) = 24。回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张N*M的表格。每个格子里写了一个数字,其中第i行第j列的那个格子里写着数为LCM(i, j)。一个4*5的表格如下:

1 2 3 4 5

2 2 6 4 10

3 6 3 12 15

4 4 12 4 20 看着这个表格,Crash想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当N和M很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和mod 20101009的值。

Input

输入的第一行包含两个正整数,分别表示N和M。

Output

输出一个正整数,表示表格中所有数的和mod 20101009的值。

  ∑∑lcm(i,j) ,i=1..n,j=1..m

=∑∑i*j/gcd(i,j) ,i=1..n,j=1..m

=∑∑∑[gcd(i,j)=d]*i*j/d ,d=1..n,i=1..n,j=1..m (枚举gcd的取值d)

=∑d*(∑∑[gcd(i,j)=1]*i*j) ,d=1..n,i=1..n/d,j=1..m/d

=∑d*(∑a*a*mu[a]*( ∑∑i*j )) ,d=1..n,a=1..n/d,i=1..n/d/a,j=1..m/d/a  (由[n=1]=∑mu[d] ,d|n可得)

a的取值只有O(n0.5)个,可以对每个取值只进行一次计算

对每个a,(n/d/a,m/d/a)的取值也是O(n0.5)个,可以用同样方式优化

∑∑i*j用等差数列求和公式可以O(1)计算

#include<cstdio>
typedef long long i64;
const int N=1e7,P=20101009;
int ps[N/5],pp=0,mu[N+5];
i64 m2[N+5];
bool isnp[N+5];
int n,m;
inline i64 f(i64 a,i64 b){
    return ((a*(a+1)>>1)%P*((b*(b+1)>>1)%P))%P;
}
inline int min(int a,int b){
    return a<b?a:b;
}
int main(){
    scanf("%d%d",&n,&m);
    if(n>m)n^=m,m^=n,n^=m;
    mu[1]=1;
    for(int i=2;i<=n;i++){
        if(!isnp[i])ps[pp++]=i,mu[i]=-1;
        for(int j=0,k;j<pp&&(k=i*ps[j])<=n;j++){
            isnp[k]=1;
            if(i%ps[j])mu[k]=-mu[i];
            else break;
        }
    }
    for(int i=1;i<=n;i++){
        m2[i]=(mu[i]*1ll*i*i+m2[i-1])%P;
        if(m2[i]<0)m2[i]+=P;
    }
    i64 ans=0;
    for(int d=1,d2;d<=n;d=d2+1){
        d2=min(n/(n/d),m/(m/d));
        int x=n/d,y=m/d;
        i64 s=0;
        for(int a=1,b;a<=x;a=b+1){
            b=min(x/(x/a),y/(y/a));
            s+=(m2[b]-m2[a-1])*f(x/a,y/a)%P;
        }
        s%=P;
        ans=(ans+(1ll*(d+d2)*(d2-d+1)>>1)%P*s%P)%P;
    }
    if(ans<0)ans+=P;
    printf("%lld\n",ans);
    return 0;
}

 

posted on 2016-05-21 14:17  nul  阅读(236)  评论(0编辑  收藏  举报