[luogu P1829] [国家集训队]Crash的数字表格 / JZPTAB
我丢 : https://www.luogu.com.cn/problem/P1829
先
假
设
n
<
m
先假设n < m
先假设n<m
∑
i
=
1
n
∑
j
=
1
m
l
c
m
(
i
,
j
)
=
∑
i
=
1
n
∑
j
=
1
n
i
×
j
gcd
(
i
,
j
)
=
∑
d
=
1
n
∑
i
=
1
⌊
n
/
d
⌋
∑
j
=
1
⌊
m
/
d
⌋
[
gcd
(
i
,
j
)
=
1
]
d
×
i
×
j
(
枚
举
d
=
g
c
d
)
=
∑
d
=
1
n
∑
i
=
1
⌊
n
/
d
⌋
∑
j
=
1
⌊
m
/
d
⌋
∑
k
∣
gcd
(
i
,
j
)
μ
(
k
)
×
d
×
i
×
j
=
∑
d
=
1
n
∑
k
=
1
⌊
n
/
d
⌋
∑
i
=
1
⌊
n
/
k
d
⌋
∑
j
=
1
⌊
m
/
k
d
⌋
μ
(
k
)
×
d
×
i
k
×
j
k
=
∑
T
=
1
n
T
×
∑
i
=
1
⌊
n
/
T
⌋
i
×
∑
j
=
1
⌊
m
/
T
⌋
j
×
∑
k
∣
T
μ
(
k
)
×
k
(
设
T
=
k
d
,
k
∗
k
∗
d
=
T
∗
k
)
\begin{aligned} & \sum_{i=1}^n\sum_{j=1}^m lcm(i,j)\\ =& \sum_{i=1}^n\sum_{j=1}^n \frac{i \times j}{\gcd(i,j)} \\ =& \sum_{d=1}^n\sum_{i=1}^{\lfloor n/d \rfloor}\sum_{j=1}^{\lfloor m/d \rfloor}[\gcd(i,j)=1]d\times i \times j (枚举d=gcd)\\ =& \sum_{d=1}^n\sum_{i=1}^{\lfloor n/d \rfloor}\sum_{j=1}^{\lfloor m/d \rfloor}\sum_{k|\gcd(i,j)}\mu(k) \times d\times i \times j \\ =& \sum_{d=1}^n\sum_{k=1}^{\lfloor n/d\rfloor}\sum_{i=1}^{\lfloor n/kd\rfloor}\sum_{j=1}^{\lfloor m/kd\rfloor}\mu(k)\times d \times ik \times jk \\ =& \sum_{T=1}^{n}T\times\sum_{i=1}^{\lfloor n/T \rfloor}i\times\sum_{j=1}^{\lfloor m/T \rfloor}j\times\sum_{k|T}\mu(k)\times k(设T=kd,k*k*d=T*k) \end{aligned}
=====i=1∑nj=1∑mlcm(i,j)i=1∑nj=1∑ngcd(i,j)i×jd=1∑ni=1∑⌊n/d⌋j=1∑⌊m/d⌋[gcd(i,j)=1]d×i×j(枚举d=gcd)d=1∑ni=1∑⌊n/d⌋j=1∑⌊m/d⌋k∣gcd(i,j)∑μ(k)×d×i×jd=1∑nk=1∑⌊n/d⌋i=1∑⌊n/kd⌋j=1∑⌊m/kd⌋μ(k)×d×ik×jkT=1∑nT×i=1∑⌊n/T⌋i×j=1∑⌊m/T⌋j×k∣T∑μ(k)×k(设T=kd,k∗k∗d=T∗k)
显
然
∑
i
=
1
⌊
n
/
T
⌋
i
和
∑
k
∣
T
μ
(
k
)
×
k
都
是
可
以
预
处
理
的
\large 显然\sum\limits_{i=1}^{\lfloor n/T \rfloor}i 和\sum_{k|T}\mu(k)\times k都是可以预处理的
显然i=1∑⌊n/T⌋i和∑k∣Tμ(k)×k都是可以预处理的
时
间
复
杂
度
貌
似
是
O
(
n
l
n
n
)
,
理
论
上
最
慢
要
跑
1.6
s
,
然
鹅
常
数
不
允
许
,
还
是
T
l
e
了
时间复杂度貌似是 O(n \ ln\ n),理论上最慢要跑1.6s,然鹅常数不允许,还是Tle了
时间复杂度貌似是O(n ln n),理论上最慢要跑1.6s,然鹅常数不允许,还是Tle了
code:
#include<bits/stdc++.h>
#define N 10000005
#define mod 20101009
#define int long long
using namespace std;
int prime[N], vis[N], mu[N], s[N], sz;
void init() {
mu[1] = 1;
for(int i = 2; i < N; i ++) {
if(!vis[i]) {
prime[++ sz] = i;
mu[i] = mod - 1;
}
for(int j = 1; j <= sz && prime[j] * i < N; j ++) {
vis[prime[j] * i] = 1;
if(i % prime[j] == 0) break;
else mu[i * prime[j]] = mod - mu[i];
}
}
for(int i = 1; i < N; i ++)
for(int j = i; j < N; j += i)
s[j] = (s[j] + mu[i] * i % mod) % mod;
}
int calc(int x) {
return (x * (x + 1) / 2) % mod;
}
int n, m;
signed main() {
init();
scanf("%lld%lld", &n, &m);
int ans = 0;
for(int T = 1; T < N; T ++) {
int ha = 0;
ans += T * calc(n / T) % mod * calc(m / T) % mod * s[T] % mod, ans %= mod;
}
printf("%lld", ans);
return 0;
}
主要是倍数那里好像优化不了了,那就换一种方法推吧,枚举倍数
∑
i
=
1
n
∑
j
=
1
m
l
c
m
(
i
,
j
)
=
∑
d
=
1
n
∑
k
=
1
⌊
n
/
d
⌋
∑
i
=
1
⌊
n
/
k
d
⌋
∑
j
=
1
⌊
m
/
k
d
⌋
μ
(
k
)
×
d
×
i
k
×
j
k
=
∑
d
=
1
n
d
∑
k
=
1
⌊
n
/
d
⌋
k
2
μ
(
k
)
∑
i
=
1
⌊
n
/
k
d
⌋
i
∑
j
=
1
⌊
m
/
k
d
⌋
j
\begin{aligned} & \sum_{i=1}^n\sum_{j=1}^m lcm(i,j)\\ =& \sum_{d=1}^n\sum_{k=1}^{\lfloor n/d\rfloor}\sum_{i=1}^{\lfloor n/kd\rfloor}\sum_{j=1}^{\lfloor m/kd\rfloor}\mu(k)\times d \times ik \times jk \\ =& \sum_{d=1}^nd\sum_{k=1}^{\lfloor n/d\rfloor}k^2\mu(k)\sum_{i=1}^{\lfloor n/kd\rfloor}i\sum_{j=1}^{\lfloor m/kd\rfloor}j\\ \end{aligned}
==i=1∑nj=1∑mlcm(i,j)d=1∑nk=1∑⌊n/d⌋i=1∑⌊n/kd⌋j=1∑⌊m/kd⌋μ(k)×d×ik×jkd=1∑ndk=1∑⌊n/d⌋k2μ(k)i=1∑⌊n/kd⌋ij=1∑⌊m/kd⌋j
这
个
东
西
∑
k
=
1
⌊
n
/
d
⌋
k
2
μ
(
k
)
∑
i
=
1
⌊
n
/
k
d
⌋
i
∑
j
=
1
⌊
m
/
k
d
⌋
j
看
起
来
很
整
除
分
块
\large 这个东西\sum\limits_{k=1}^{\lfloor n/d\rfloor}k^2\mu(k)\sum\limits_{i=1}^{\lfloor n/kd\rfloor}i\sum\limits_{j=1}^{\lfloor m/kd\rfloor}j\\ 看起来很整除分块
这个东西k=1∑⌊n/d⌋k2μ(k)i=1∑⌊n/kd⌋ij=1∑⌊m/kd⌋j看起来很整除分块
设
g
(
n
,
m
)
=
∑
i
=
1
n
i
∑
j
=
1
m
j
,
这
个
显
然
可
以
O
(
1
)
算
\large设g(n,m)=\sum\limits_{i=1}^{ n}i\sum\limits_{j=1}^{m}j,这个显然可以O(1)算
设g(n,m)=i=1∑nij=1∑mj,这个显然可以O(1)算
即
∑
k
=
1
⌊
n
/
d
⌋
k
2
μ
(
k
)
∑
i
=
1
⌊
n
/
k
d
⌋
i
∑
j
=
1
⌊
m
/
k
d
⌋
=
∑
k
=
1
⌊
n
/
d
⌋
k
2
μ
(
k
)
g
(
⌊
n
/
d
k
⌋
,
⌊
m
/
d
k
⌋
)
显
然
可
以
整
除
分
块
即\large \sum\limits_{k=1}^{\lfloor n/d\rfloor}k^2\mu(k)\sum\limits_{i=1}^{\lfloor n/kd\rfloor}i\sum\limits_{j=1}^{\lfloor m/kd\rfloor}\\=\sum\limits_{k=1}^{\lfloor n/d\rfloor}k^2\mu(k)g(\lfloor n/dk\rfloor,\lfloor m/dk\rfloor)显然可以整除分块
即k=1∑⌊n/d⌋k2μ(k)i=1∑⌊n/kd⌋ij=1∑⌊m/kd⌋=k=1∑⌊n/d⌋k2μ(k)g(⌊n/dk⌋,⌊m/dk⌋)显然可以整除分块
再
设
f
(
n
,
m
)
=
∑
k
=
1
n
k
2
μ
(
k
)
g
(
⌊
n
/
k
⌋
,
⌊
m
/
k
⌋
)
\large 再设f(n, m)=\sum\limits_{k=1}^{n}k^2\mu(k)g(\lfloor n/k\rfloor,\lfloor m/k\rfloor)
再设f(n,m)=k=1∑nk2μ(k)g(⌊n/k⌋,⌊m/k⌋)
那
么
∑
d
=
1
n
d
∑
k
=
1
⌊
n
/
d
⌋
k
2
μ
(
k
)
∑
i
=
1
⌊
n
/
k
d
⌋
i
∑
j
=
1
⌊
m
/
k
d
⌋
j
=
∑
d
=
1
n
d
×
f
(
⌊
n
/
d
⌋
,
⌊
m
/
d
⌋
)
\large 那么\sum\limits_{d=1}^nd\sum\limits_{k=1}^{\lfloor n/d\rfloor}k^2\mu(k)\sum\limits_{i=1}^{\lfloor n/kd\rfloor}i\sum\limits_{j=1}^{\lfloor m/kd\rfloor}j\\ \ \ \ \ =\sum\limits_{d=1}^nd\times f(\lfloor n/d \rfloor, \lfloor m/d \rfloor)
那么d=1∑ndk=1∑⌊n/d⌋k2μ(k)i=1∑⌊n/kd⌋ij=1∑⌊m/kd⌋j =d=1∑nd×f(⌊n/d⌋,⌊m/d⌋)
就
是
两
个
乘
除
分
块
套
在
一
起
时
间
复
杂
度
O
(
n
2
)
=
O
(
n
)
就是两个乘除分块套在一起时间复杂度 O( \sqrt n^2)=O(n)
就是两个乘除分块套在一起时间复杂度O(n2)=O(n)
禁止套娃
code:
#include<bits/stdc++.h>
#define N 10000005
#define mod 20101009
#define int long long
using namespace std;
int prime[N], vis[N], mu[N], s[N], sz;
inline void init() {
mu[1] = 1;
for(int i = 2; i < N; i ++) {
if(!vis[i]) {
prime[++ sz] = i;
mu[i] = mod - 1;
}
for(register int j = 1; j <= sz && prime[j] * i < N; j ++) {
vis[prime[j] * i] = 1;
if(i % prime[j] == 0) break;
else mu[i * prime[j]] = mod - mu[i];
}
}
for(int i = 1; i < N; i ++) s[i] = (s[i - 1] + i * i % mod * mu[i]) % mod;
}
int g(int x, int y) {
return (x * (x + 1) / 2 % mod) * (y * (y + 1) / 2 % mod) % mod;
}
int f(int x, int y) {
int ret = 0;
for(int l = 1, r = 0; l <= x; l = r + 1) {
r = min(x / (x / l), y / (y / l));
ret += (s[r] - s[l - 1] + mod) * g(x / l, y / l) % mod, ret %= mod;
}
return ret;
}
int n, m;
signed main() {
init();
scanf("%lld%lld", &n, &m);
if(n > m) swap(n, m);
int ans = 0;
for(int l = 1, r = 0; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans += (r - l + 1) * (l + r) / 2 % mod * f(n / l, m / l) % mod, ans %= mod;
}
printf("%lld", ans);
return 0;
}