提示:原 P1829 半数集问题 已经迁移至 P1028 数的计算
今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数 a a 和b b , LCM(a,b) L C M ( a , b ) 表示能同时整除 a a 和b b 的最小正整数。例如, LCM(6,8)=24 L C M ( 6 , 8 ) = 24 。
回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张 N∗M N ∗ M 的表格。每个格子里写了一个数字,其中第 i i 行第j j 列的那个格子里写着数为 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 和M M 很大时,Crash就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash只想知道表格里所有数的和 mod 20101009 m o d 20101009 的值。
输入的第一行包含两个正整数,分别表示 N N 和M M 。
输出一个正整数,表示表格中所有数的和 mod 20101009 m o d 20101009 的值。
4 5
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)=∑k|xk×μ(k) f ( x ) = ∑ k | x k × μ ( k ) 。
最后这个式子与 μ μ 一点关系都没有, 筛都不需要筛出来…
我们对 x×f(x) x × f ( x ) 做一个前缀和, 套上下底分块就可以 AC A C 了。
总复杂度 O(N+N−−√) O ( N + N ) 。
代码如下:
#include
#include
#include
#include
#include
#include
#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);
}