原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2154
Crash的数字表格
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
4 5
Sample Output
122
【数据规模和约定】
100%的数据满足N, M ≤ 10^7。
题解
初始式子:
显然有 lcm(a,b)=abgcd(a,b) l c m ( a , b ) = a b g c d ( a , b ) :
基本操作之更该枚举项+提取公因数:
我们把 ∑⌊nd⌋i=1∑⌊md⌋j=1ij[1=gcd(i,j)] ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i j [ 1 = g c d ( i , j ) ] 提出来单独调♂教,设 F(x,y)=∑xi=1∑yj=1ij[1=gcd(i,j)] F ( x , y ) = ∑ i = 1 x ∑ j = 1 y i j [ 1 = g c d ( i , j ) ] :
代回原式:
自然想到将 dd′ d d ′ 替换为 T T 并枚举:
设 T=i×p[j],f(T)=T∑d′|Tμ(d′)d′,g(T)=∑d′|Tμ(d′)d′ T = i × p [ j ] , f ( T ) = T ∑ d ′ | T μ ( d ′ ) d ′ , g ( T ) = ∑ d ′ | T μ ( d ′ ) d ′ ,我们单独拿出 g(T) g ( T ) 讨论如何线筛:
1.当 T T 为素数时,的取值只能为 1,T 1 , T ,此时 g(T)=1−T g ( T ) = 1 − T ;
2.当 T T 拥有多个最小质因子时,由于没有新增质因子,所以不影响我们的枚举,此时;
3.当 T T 的最小质因子只有一个时,在原来枚举的所有因数的基础上,我们还多了一堆“原来的因数”,因为多了一个质因数之后, μ(d′×p[j])=−μ(d′) μ ( d ′ × p [ j ] ) = − μ ( d ′ ) ,所以这个最小质因子的贡献为 −p[j]×g(i) − p [ j ] × g ( i ) ,此时 g(T)=g(i)×(1−p[j]) g ( T ) = g ( i ) × ( 1 − p [ j ] ) 。
在筛完 g(T) g ( T ) 后,依次乘以 T T 即可得到。
代码实现时,对 f(T) f ( T ) 求前缀和,套上下底分块即可实现 O(n−−√) O ( n ) 求解。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1e7+5,mod=20101009;
int n,m,p[M/3];
ll f[M],ans,s1,s2;
bool isp[M];
void pre()
{
f[1]=1;int t;
for(int i=2;i<=n;++i)
{
if(!isp[i])p[++p[0]]=i,f[i]=1-i;
for(int j=1;j<=p[0];++j)
{
t=i*p[j];if(t>n)break;
isp[t]=1;
if(i%p[j]==0){f[t]=f[i];break;}
f[t]=f[i]*(1-p[j]);
}
f[i]=(f[i-1]+f[i]*i%mod)%mod;
}
}
void in(){scanf("%d%d",&n,&m);}
void ac()
{
if(n>m)swap(n,m);pre();
for(int l=1,r;l<=n;l=r+1)
{r=min(n/(n/l),m/(m/l));s1=1ll*(1+n/l)*(n/l)/2%mod;s2=1ll*(1+m/l)*(m/l)/2%mod;(ans+=(f[r]-f[l-1])*((s1*s2)%mod)%mod)%=mod;}
printf("%lld",(ans+mod)%mod);
}
int main(){in();ac();}