题目背景
提示:原 P1829 半数集问题 已经迁移至 P1028 数的计算
题目描述
今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数a和b,LCM(a, b)表示能同时整除a和b的最小正整数。例如,LCM(6, 8) = 24。
回到家后,Crash还在想着课上学的东西,为了研究最小公倍数,他画了一张NM的表格。每个格子里写了一个数字,其中第i行第j列的那个格子里写着数为LCM(i, j)。一个45的表格如下:
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只想知道表格里所有数的和mod20101009的值。
输入输出格式
输入格式:
输入的第一行包含两个正整数,分别表示N和M。
输出格式:
输出一个正整数,表示表格中所有数的和mod20101009的值。
思路讲解
这是一道莫比乌斯反演和数论分块的练手题。
推一波公式:
ans=∑i=1n∑j=1m[i,j]=∑i=1n∑j=1mi⋅j(i,j)\text{ans}=\sum_{i=1}^n\sum_{j=1}^m[i,j]=\sum_{i=1}^n\sum_{j=1}^m\frac{i\cdot j}{(i,j)}ans=i=1∑nj=1∑m[i,j]=i=1∑nj=1∑m(i,j)i⋅j
令d=(i,j)d=(i,j)d=(i,j),则必有:(id,jd)=1(\frac{i}{d},\frac{j}{d})=1(di,dj)=1,于是:
=∑i=1n∑j=1m∑d∣i∧d∣j∧(id,jd)=1i⋅jd=\sum_{i=1}^n\sum_{j=1}^m\sum_{d|i∧d|j∧(\frac{i}{d},\frac{j}{d})=1}\frac{i\cdot j}{d}=i=1∑nj=1∑md∣i∧d∣j∧(di,dj)=1∑di⋅j
考虑用ddd枚举i,ji,ji,j,则我们可以把和式提前:
=∑d=1min(n,m)d∑i=1⌊nd⌋∑j=1⌊md⌋[(i,j)=1]⋅i⋅j=\sum_{d=1}^{\min(n,m)}d\sum_{i=1}^{\left\lfloor\frac{n}{d}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{d}\right\rfloor}[(i,j)=1]\cdot i\cdot j=d=1∑min(n,m)di=1∑⌊dn⌋j=1∑⌊dm⌋[(i,j)=1]⋅i⋅j
我们令:
Sum(n,m)=∑i=1n∑j=1m[(i,j=1)]⋅i⋅j\text{Sum}(n,m)=\sum_{i=1}^n\sum_{j=1}^m[(i,j=1)]\cdot i\cdot jSum(n,m)=i=1∑nj=1∑m[(i,j=1)]⋅i⋅j
则:
ans=∑d=1min(n,m)d⋅Sum(⌊nd⌋,⌊md⌋)\text{ans}=\sum_{d=1}^{\min(n,m)}d\cdot \text{Sum}(\left\lfloor\frac{n}{d}\right\rfloor,\left\lfloor\frac{m}{d}\right\rfloor)ans=d=1∑min(n,m)d⋅Sum(⌊dn⌋,⌊dm⌋)
考虑求Sum(n,m)\text{Sum}(n,m)Sum(n,m):
根据套路,套莫比乌斯反演:
=∑d=1min(n,m)∑d∣in∑d∣jmμ(d)⋅i⋅j=\sum_{d=1}^{\min(n,m)}\sum_{d|i}^n\sum_{d|j}^mμ(d)\cdot i\cdot j=d=1∑min(n,m)d∣i∑nd∣j∑mμ(d)⋅i⋅j
可以将μ(d)μ(d)μ(d)提前:
=∑d=1min(n,m)μ(d)d2∑i=1⌊nd⌋∑j=1⌊md⌋i⋅j=\sum_{d=1}^{\min(n,m)}μ(d)d^2\sum_{i=1}^{\left\lfloor\frac{n}{d}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{d}\right\rfloor}i\cdot j=d=1∑min(n,m)μ(d)d2i=1∑⌊dn⌋j=1∑⌊dm⌋i⋅j
由拉格朗日插值公式,我们可以知道:
L(n,m)=∑i=1n∑j=1mij=∑i=1ni∑j=1mj=n(n+1)2×m(m+1)2L(n,m)=\sum_{i=1}^n\sum_{j=1}^mij=\sum_{i=1}^ni\sum_{j=1}^mj=\frac{n(n+1)}{2}×\frac{m(m+1)}{2}L(n,m)=i=1∑nj=1∑mij=i=1∑nij=1∑mj=2n(n+1)×2m(m+1)
于是:
Sum(n,m)=∑d=1min(n,m)μ(d)d2L(⌊nd⌋,⌊md⌋)\text{Sum}(n,m)=\sum_{d=1}^{\min(n,m)}μ(d)d^2L(\left\lfloor\frac{n}{d}\right\rfloor,\left\lfloor\frac{m}{d}\right\rfloor)Sum(n,m)=d=1∑min(n,m)μ(d)d2L(⌊dn⌋,⌊dm⌋)
于是可以数论分块求。
定一个前缀和,令:
Sd=∑i=1dμ(i)i2S_d=\sum_{i=1}^dμ(i)i^2Sd=i=1∑dμ(i)i2
就可以了。
时间复杂度:
我估计是O(min(n,m))\mathcal O(\min(n,m))O(min(n,m))的吧!有一定的常数。
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 1E+7 + 1;
const int M = 20101009;
int S[N], prime[N/10], Mu[N], n, m, tot;
bool flag[N];
void sieve() {
Mu[1] = 1;
for(int i = 2; i <= min(n, m); i++) {
if(!flag[i]) prime[++tot] = i, Mu[i] = -1;
for(int j = 1; j <= tot && i * prime[j] <= min(n, m); j++) {
flag[i * prime[j]] = 1;
if(i % prime[j] == 0) { Mu[i * prime[j]] = 0; break; }
Mu[i * prime[j]] = -Mu[i];
}
}
for(int d = 1; d <= min(n, m); d++)
S[d] = (S[d - 1] + 1LL * d * d % M * (Mu[d] + M)) % M;
}
int G(int n, int m) {
return (1LL * n * (n + 1) / 2 % M) * (1LL * m * (m + 1) / 2 % M) % M;
}
int Sum(int n, int m) {
int res = 0;
for(int l = 1, r; l <= min(n, m); l = r + 1) {
r = min(n / (n / l), m / (m / l));
res = (res + 1LL * (S[r] - S[l - 1] + M) * G(n / l, m / l) % M) % M;
}
return res;
}
int cal(int n, int m) {
int res = 0;
for(int l = 1, r; l <= min(n, m); l = r + 1) {
r = min(n / (n / l), m / (m / l));
res = (res + 1LL * (l + r) * (r - l + 1) / 2 % M * Sum(n / l, m / l) % M) % M;
}
return res;
}
int main()
{
scanf("%d %d", &n, &m);
sieve();
printf("%d", cal(n, m));
return 0;
}