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的值。
Sample Input
Sample Output
122
【数据规模和约定】
100%的数据满足N, M ≤ 10^7。
我们是能推出来这个公式的,这种情况的话我们是能预处理 i^2*u[i]的这个东西,对于sum()里面的东西我们是可以分块来做的,所以总的时间复杂度就是o(n) 对于o(n)的话这个问题是可以解决的但是要是多组数据的话o(n)的时间复杂度就不行了,那是后我们再优化这个东西,我们把D=d*i;
然后我们发现后面的东西是个积性函数,对于积性函数的话我们是可以利用线性筛把这个东西预处理出来的,为什么后面的东西是积性函数。
现在证为什么为积性函数: 首先我们知道的东西是:若? ? ,?(?)均为积性函数则ℎ (n) = ?(n) ?(?)也是积性函数。
然后我们知道的东西就是积性函数的约数和也是积性函数(这个东西好像很明显,但是但是我蠢啊!) 具体细节请读者自己思考。
然后我们就可以用一个线性筛的方法然后把这个问题完美的解决。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=20101009;
const int N=1e7+4;
int vis[N],prime[N];
ll g[N];
ll cnt;
void Init()
{
memset(vis,0,sizeof(vis));
g[1] = 1;
cnt = 0;
for(int i=2; i<N; i++)
{
if(!vis[i])
{
prime[cnt++] = i;
g[i]=(long long)(1-i)*(i);
g[i]=g[i]%mod;
}
for(int j=0; j<cnt&&i*prime[j]<N; j++)
{
vis[i*prime[j]] = 1;
if(i%prime[j]) g[i*prime[j]]=(g[i]*g[prime[j]])%mod;
else
{
g[i*prime[j]]=(g[i]*prime[j])%mod;
break;// 为何这个会break,因为是为了解决时间。但是有的读者会担心是不是以后i*prime[j]会不出现啊,我们可以想一下,i要么是和prime[j]相同的素数,这个时候我们可以break;除了这种情况i就是含有的因子和prime[j]相同,这个时候我们只是得到了i*prime[j]后面的所有的数都可以得到,我们可以用假设的方法。
}
}
}
for(int i=1;i<N;i++)
{
g[i]=(g[i-1]+g[i])%mod;
}
}
ll result(ll x,ll y)
{
return (((x*(x+1)/2)%mod)*((y*(y+1)/2)%mod))%mod;
}
void work(int n,int m)
{
if(n>m) swap(n,m);
int r;
ll ans=0;
for(int i=1;i<=n;i=r+1)
{
r=min(n/(n/i),m/(m/i));
ans+=(result(n/i,m/i)*((g[r]-g[i-1]+mod)%mod))%mod;
ans%=mod;
}
printf("%lld\n",(ans+mod)%mod);
}
int main()
{
int n,m;
Init();
scanf("%d%d",&n,&m);
work(n,m);
}