1<=n,m<=500000
题解:
为什么这么多题目的名字叫求和?
这题其实有很多种做法,但是用一个非常简单的性质就可以做出来。。。
s
(
p
q
)
=
∑
i
∣
p
∑
j
∣
q
[
(
i
,
j
)
=
1
]
∗
i
∗
q
/
j
s(pq)=\sum_{i|p}\sum_{j|q}[(i,j)=1]*i*{q/j}
s(pq)=∑i∣p∑j∣q[(i,j)=1]∗i∗q/j
证明之前写过:
https://blog.csdn.net/Cold_Chair/article/details/78186552
那么就开始反演:
F
(
n
,
m
)
(
n
<
m
)
F(n,m)(n<m)
F(n,m)(n<m)
=
∑
i
=
1
n
∑
j
=
1
m
μ
(
i
)
∗
μ
(
j
)
∗
∑
x
∣
i
∑
y
∣
j
[
(
x
,
y
)
=
1
]
x
j
/
y
=\sum_{i=1}^n \sum_{j=1}^m\mu(i)*\mu(j)*\sum_{x|i}\sum_{y|j}[(x,y)=1]xj/y
=∑i=1n∑j=1mμ(i)∗μ(j)∗∑x∣i∑y∣j[(x,y)=1]xj/y
中间过程略。
=
∑
d
=
1
n
μ
(
d
)
∗
d
(
∑
i
=
1
n
/
d
∑
j
=
1
m
/
d
μ
(
d
∗
i
∗
x
)
∗
i
)
(
∑
x
=
1
n
/
d
/
i
∑
y
=
1
m
/
d
/
j
μ
(
d
∗
j
∗
y
)
∗
y
)
=\sum_{d=1}^n\mu(d)*d(\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}\mu(d*i*x)*i)(\sum_{x=1}^{n/d/i}\sum_{y=1}^{m/d/j}\mu(d*j*y)*y)
=∑d=1nμ(d)∗d(∑i=1n/d∑j=1m/dμ(d∗i∗x)∗i)(∑x=1n/d/i∑y=1m/d/jμ(d∗j∗y)∗y)
这个玩意做几次倍数统计就行了。
Code:
#include<cstdio>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
int n, m;
const int N = 5e5 + 5;
int bz[N], p[N], mu[N];
void sieve(int n) {
fo(i, 2, n) {
if(!bz[i]) p[++ p[0]] = i, mu[i] = -1;
for(int j = 1; i * p[j] <= n; j ++) {
int k = i * p[j];
bz[k] = 1;
if(i % p[j] == 0) {
mu[k] = 0; break;
}
mu[k] = -mu[i];
}
}
mu[1] = 1;
}
const int mo = 998244353;
ll a[N], b[N];
int main() {
scanf("%d %d", &n, &m);
if(n > m) swap(n, m);
sieve(m);
fo(i, 1, n) for(int j = i; j <= n; j += i) a[i] += mu[j];
fo(i, 1, m) for(int j = i, k = 1; j <= m; j += i, k ++)
b[i] += k * mu[j], b[i] %= mo;
ll ans = 0;
fo(i, 1, n) {
ll s1 = 0, s2 = 0;;
for(int j = i, k = 1; j <= n; j += i, k ++)
s1 = (s1 + (ll) k * a[j]) % mo;
for(int j = i; j <= m; j += i) s2 = (s2 + b[j]) % mo;
ans += s1 * s2 % mo * mu[i] * i % mo;
}
printf("%lld", (ans % mo + mo) % mo);
}