洛谷传送门
BZOJ传送门
题目背景
提示:原 P1829 半数集问题 已经迁移至 P1028 数的计算
题目描述
今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数 a a 和, LCM(a,b) L C M ( a , b ) 表示能同时整除 a a 和的最小正整数。例如, LCM(6,8)=24 L C M ( 6 , 8 ) = 24 。
回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张 N∗M N ∗ M 的表格。每个格子里写了一个数字,其中第 i i 行第列的那个格子里写着数为 LCM(i,j) L C M ( i , j ) 。一个 4∗5 4 ∗ 5 的表格如下:
1 2 3 4 5
2 2 6 4 10
3 6 3 12 15
4 4 12 4 20
看着这个表格,Crash想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当 N N 和很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和 mod 20101009 m o d 20101009 的值。
输入输出格式
输入格式:
输入的第一行包含两个正整数,分别表示 N N 和。
输出格式:
输出一个正整数,表示表格中所有数的和 mod 20101009 m o d 20101009 的值。
输入输出样例
输入样例#1:
4 5
输出样例#1:
122
说明
30%的数据满足 N,M≤103 N , M ≤ 10 3 。
70%的数据满足 N,M≤105 N , M ≤ 10 5 。
100%的数据满足 N,M≤107 N , M ≤ 10 7 。
解题分析
首先推一波式子,这里我们不妨设
n<=m
n
<=
m
:
现在我们单独拿出右边的一堆进行处理。设 F(x,y)=∑xi=1∑yj=1ij[gcd(i,j)=1](x<=y) F ( x , y ) = ∑ i = 1 x ∑ j = 1 y i j [ g c d ( i , j ) = 1 ] ( x <= y ) ,那么
将这个式子代回原式,得到:
看起来太凌乱, 而且已经没法化简了, 于是我们套路地更改枚举项,设 T=kd T = k d :
我们要做的最后一件事就是快速求出 ∑k|Tk×μ(k) ∑ k | T k × μ ( k ) 了(因为前面可以下底分块快速求)。
考虑 f(x)=∑k|xk×μ(k) f ( x ) = ∑ k | x k × μ ( k ) 。
- 当 x x 为质数时, 显然。
- 当 x=pri×m x = p r i × m 时且 pri∤m p r i ∤ m 时, 除了原来的因数, 我们还引入了 pri×a1,pri×a2...(a1,a2...|m) p r i × a 1 , p r i × a 2 . . . ( a 1 , a 2 . . . | m ) 这些因数, 而 μ(pri×a1)=−μ(a1) μ ( p r i × a 1 ) = − μ ( a 1 ) , 所以 f(x)=−pri×f(m)+f(m) f ( x ) = − p r i × f ( m ) + f ( m ) 。
- 当 x=pri×m x = p r i × m 时且 pri|m p r i | m 时, 没有新的约数, 而 μ(pri2)=μ(pri2×a1)...=0 μ ( p r i 2 ) = μ ( p r i 2 × a 1 ) . . . = 0 , 所以 f(pri×m)=f(m) f ( p r i × m ) = f ( m ) 。
最后这个式子与 μ μ 一点关系都没有, 筛都不需要筛出来…
我们对 x×f(x) x × f ( x ) 做一个前缀和, 套上下底分块就可以 AC A C 了。
总复杂度 O(N+N−−√) O ( N + N ) 。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <cmath>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define ll long long
#define MOD 20101009ll
#define MX 10000500
int pri[MX], pcnt, n, m;
ll F[MX], sum[MX];
bool npr[MX];
void get()
{
F[1] = sum[1] = 1;
R int i, j, tar;
for (i = 2; i <= n; ++i)
{
if(!npr[i]) pri[++pcnt] = i, F[i] = 1 - i;
for (j = 1; j <= pcnt; ++j)
{
tar = pri[j] * i;
if(tar > n) break;
npr[tar] = true;
if(!(i % pri[j])) {F[tar] = F[i]; break;}
F[tar] = F[i] * (1 - pri[j]);
}
sum[i] = (sum[i - 1] + F[i] * i % MOD + MOD) % MOD;
}
}
int main(void)
{
scanf("%d%d", &n, &m);
if(n > m) std::swap(n, m);
get(); ll ans = 0;
for (R int lef = 1, rig, sum1, sum2; lef <= n; lef = rig + 1)
{
rig = std::min(n / (n / lef), m / (m / lef));
sum1 = 1ll * (n / lef) * (n / lef + 1) / 2 % MOD;
sum2 = 1ll * (m / lef) * (m / lef + 1) / 2 % MOD;
ans += 1ll * sum1 * sum2 % MOD * (sum[rig] - sum[lef - 1] + MOD) % MOD;
ans %= MOD;
}
printf("%lld", (ans % MOD + MOD) % MOD);
}