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 ≤ 107。
jzp神题
似乎跟gcd问题有点像,实际上区别较大
求sigma(lcm(x,y))(1<=x<=n,1<=y<=m)
明显的,枚举最大公约数d之后就变为sigma(x*y)(1<=x<=n/d,1<=y<=m/d,gcd(x,y)=1)=f[d]
那么gcd=d的lcm和为f[d]*d
虽然d的取值在[1,n]但是n/d和 m/d的取值是sqrt(n)级别的
就可以用解决gcd问题的类似的方法来解决这个假设l到r的n/d和m/d是相等的,我们可以将其一起统计
对答案的贡献为sigma(x*y)(1<=x<=n/d,1<=y<=m/d,gcd(x,y)=1)*(r-l+1)*(l+r)/2
接下来问题是如何快速求sigma(x*y)(1<=x<=n/d,1<=y<=m/d,gcd(x,y)=1)
设这个为g[a,b]好了
那么类似的我们可以求f[a,b,k]=sigma(xy)(1<=x<=a,1<=y<=b,gcd[x,y]=wk)
明显f[a,b,k]可以直接算
f[a,b,k]=(1+..+a/k)*k*(1+..+b/k)*k=(a/k+1)*(a/k)/2*k*(b/k+1)*(b/k)/2*k
显然g[a,b]=sigma(mob(k)*f[a,b,k])
问题来了
每个f[a,b,k]都含有k^2这一项,这个变换不连续……
但是显然的f[a,b,k]只会乘mob(k)
那我们定义o(i)=mob(i)*i*i,w(a,b,k)=(a/k+1)*(a/k)/2*(b/k+1)*(b/k)/2
g[a,b]=sigma(o(k)*w(a,b,k))
发现w(a,b,k)是分sqrt段的
o可以预处理出来
就可以sqrt级别时间求g函数了
那么复杂度为O(sqrt(n)*sqrt(n))=O(n)
program bzoj2154;
const
maxn=20101009;
var
n,m,i,j,k:longint;
ans:int64;
f:array [0..10000001] of longint;
p:array [0..10000001] of int64;
function calc (a,b:longint):int64;inline;
var
i,k:longint;
ans:int64;
begin
ans:=0;
i:=1;
while i<=a do
begin
if a div (a div i)<b div (b div i) then
k:=a div (a div i)
else
k:=b div (b div i);
ans:=(ans+(p[k]-p[i-1]) mod maxn*
(int64(a div i+1)*(a div i) div 2 mod maxn) mod maxn*
(int64(b div i+1)*(b div i) div 2 mod maxn) mod maxn) mod maxn;
i:=k+1;
end;
exit(ans);
end;
begin
read(n,m);
if n>m then
begin
n:=n xor m;
m:=n xor m;
n:=n xor m;
end;
for i:=2 to n do
if f[i]=0 then
for j:=i to n div i do
f[i*j]:=i;
p[1]:=1;
for i:=2 to n do
if f[i]=0 then p[i]:=-1
else
begin
k:=i div f[i];
if k mod f[i] = 0 then p[i]:=0
else p[i]:=-p[k];
end;
for i:=1 to n do
p[i]:=p[i]*i*i mod maxn;
for i:=2 to n do
p[i]:=(p[i]+p[i-1]) mod maxn;
i:=1;
while i<=n do
begin
if n div (n div i)<m div (m div i) then
k:=n div (n div i)
else
k:=m div (m div i);
ans:=(ans+int64(k-i+1)*(k+i) div 2 mod maxn * calc(n div i,m div i)) mod maxn;
i:=k+1;
end;
ans:=(ans + maxn) mod maxn;
writeln(ans);
end.