提示:原 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 = 1 n ∑ j = 1 m [ i , j ] = ∑ i = 1 n ∑ j = 1 m i ⋅ 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),则必有: ( i d , j d ) = 1 (\frac{i}{d},\frac{j}{d})=1 (di,dj)=1,于是:
= ∑ i = 1 n ∑ j = 1 m ∑ d ∣ i ∧ d ∣ j ∧ ( i d , j d ) = 1 i ⋅ j d =\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
考虑用 d d d枚举 i , j i,j i,j,则我们可以把和式提前:
= ∑ d = 1 min ( n , m ) d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ ( 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 = 1 n ∑ j = 1 m [ ( i , j = 1 ) ] ⋅ i ⋅ j \text{Sum}(n,m)=\sum_{i=1}^n\sum_{j=1}^m[(i,j=1)]\cdot i\cdot j Sum(n,m)=i=1∑nj=1∑m[(i,j=1)]⋅i⋅j
则:
ans = ∑ d = 1 min ( n , m ) d ⋅ Sum ( ⌊ n d ⌋ , ⌊ m d ⌋ ) \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 = 1 min ( n , m ) ∑ d ∣ i n ∑ d ∣ j m μ ( 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 = 1 min ( n , m ) μ ( d ) d 2 ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ 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 = 1 n ∑ j = 1 m i j = ∑ i = 1 n i ∑ j = 1 m j = n ( n + 1 ) 2 × m ( m + 1 ) 2 L(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 = 1 min ( n , m ) μ ( d ) d 2 L ( ⌊ n d ⌋ , ⌊ m d ⌋ ) \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⌋)
于是可以数论分块求。
定一个前缀和,令:
S d = ∑ i = 1 d μ ( i ) i 2 S_d=\sum_{i=1}^dμ(i)i^2 Sd=i=1∑dμ(i)i2
就可以了。
时间复杂度:
我估计是 O ( min ( n , m ) ) \mathcal O(\min(n,m)) O(min(n,m))的吧!有一定的常数。
#include
#include
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;
}