题目
分析
先考虑无
a
a
a 限制,不妨设
n
≥
m
n \geq m
n≥m:
∑
i
=
1
n
∑
j
=
1
m
σ
(
gcd
(
i
,
j
)
)
=
∑
d
=
1
m
σ
(
d
)
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
⌊
m
d
⌋
[
gcd
(
i
,
j
)
=
1
]
=
∑
d
=
1
m
σ
(
d
)
∑
k
=
1
⌊
m
d
⌋
μ
(
k
)
⌊
n
d
k
⌋
⌊
m
d
k
⌋
\begin{aligned}&\sum_{i = 1}^n\sum_{j = 1}^m \sigma(\gcd(i, j)) \\ =&\sum_{d = 1}^m \sigma(d)\sum_{i = 1}^{\left\lfloor\frac{n}{d}\right\rfloor}\sum_{j = 1}^{\left\lfloor\frac{m}{d}\right\rfloor}[\gcd(i, j)=1] \\=&\sum_{d = 1}^m \sigma(d)\sum_{k=1}^{\left\lfloor\frac{m}{d}\right\rfloor}\mu(k)\left\lfloor\frac{n}{dk}\right\rfloor\left\lfloor\frac{m}{dk}\right\rfloor\end{aligned}
==i=1∑nj=1∑mσ(gcd(i,j))d=1∑mσ(d)i=1∑⌊dn⌋j=1∑⌊dm⌋[gcd(i,j)=1]d=1∑mσ(d)k=1∑⌊dm⌋μ(k)⌊dkn⌋⌊dkm⌋ 枚举
T
=
d
k
T=dk
T=dk:
∑
d
=
1
m
σ
(
d
)
∑
k
=
1
⌊
m
d
⌋
μ
(
k
)
⌊
n
d
k
⌋
⌊
m
d
k
⌋
=
∑
T
=
1
m
⌊
n
T
⌋
⌊
m
T
⌋
∑
d
∣
T
σ
(
d
)
μ
(
T
d
)
\begin{aligned}&\sum_{d = 1}^m \sigma(d)\sum_{k=1}^{\left\lfloor\frac{m}{d}\right\rfloor}\mu(k)\left\lfloor\frac{n}{dk}\right\rfloor\left\lfloor\frac{m}{dk}\right\rfloor\\=&\sum_{T=1}^m\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor\sum_{d|T}\sigma(d)\mu\left(\frac{T}{d}\right)\end{aligned}
=d=1∑mσ(d)k=1∑⌊dm⌋μ(k)⌊dkn⌋⌊dkm⌋T=1∑m⌊Tn⌋⌊Tm⌋d∣T∑σ(d)μ(dT) 令
f
(
x
)
=
∑
d
∣
x
σ
(
d
)
μ
(
x
d
)
f(x)=\sum_{d|x}\sigma(d)\mu\left(\frac{x}{d}\right)
f(x)=∑d∣xσ(d)μ(dx),如果我们知道
f
f
f 的前缀和,原式就能
O
(
m
)
O(\sqrt m)
O(m) 求。
接下来考虑 a a a 的限制,即原式中的 σ ( d ) \sigma(d) σ(d) 必须不大于 a a a。不难发现 a a a 的增大会使更多的 σ ( d ) \sigma(d) σ(d) 可以产生贡献,离线询问后对按 a a a 排序,我们枚举这些 σ ( d ) \sigma(d) σ(d) 以及 d d d 的倍数 x x x,并对相应的 f ( x ) f(x) f(x) 进行修改,用树状数组维护即可。
时间复杂度 O ( n ln n log n + q n log n ) O(n\ln n \log n + q \sqrt n \log n) O(nlnnlogn+qnlogn)。
代码
线性筛积性函数(约数和函数)可见 线性筛 [约数个数/约数和]。
模
2
2
2 的幂次好评。
#include <bits/stdc++.h>
typedef long long LL;
const int MAXN = 100000;
const int MAXQ = 20000;
int Q;
struct Query {
int N, M, A, i;
} P[MAXQ + 5];
int Ans[MAXN + 5];
bool Comp(Query i, Query j) {
return i.A < j.A;
}
struct BIT {
#define lowbit(x) ((x) & (-(x)))
int A[MAXN + 5];
void Update(int pos, int d) {
for (int i = pos; i <= MAXN; i += lowbit(i))
A[i] += d;
}
int GetSum(int pos) {
int ret = 0;
for (int i = pos; i > 0; i -= lowbit(i))
ret += A[i];
return ret;
}
#undef lowbit
} T;
int Mu[MAXN + 5];
LL Sigma[MAXN + 5], Low[MAXN + 5];
bool Vis[MAXN + 5];
std::vector<int> Primes;
std::vector<std::pair<LL, int> > Sig;
void Init(int n) {
Mu[1] = Sigma[1] = 1;
for (int i = 2; i <= n; i++) {
if (!Vis[i]) {
Mu[i] = -1, Sigma[i] = Low[i] = i + 1;
Primes.push_back(i);
}
for (int j = 0, k = 0; j < (int)Primes.size() && i * Primes[j] <= n; j++) {
Vis[k = i * Primes[j]] = true;
if (i % Primes[j] == 0) {
Mu[k] = 0;
Low[k] = 1 + Low[i] * Primes[j];
Sigma[k] = Sigma[i] / Low[i] * Low[k];
break;
}
Mu[k] = -Mu[i];
Low[k] = 1 + Primes[j];
Sigma[k] = Sigma[i] * Sigma[Primes[j]];
}
}
for (int i = 1; i <= n; i++)
Sig.push_back(std::make_pair(Sigma[i], i));
std::sort(Sig.begin(), Sig.end());
}
int Calc(int n, int m) {
if (n < m) std::swap(n, m);
int ret = 0;
for (int lft = 1, rgt = 1; lft <= m; lft = rgt + 1) {
rgt = std::min(n / (n / lft), m / (m / lft));
ret += (n / lft) * (m / lft) * (T.GetSum(rgt) - T.GetSum(lft - 1));
}
return ret;
}
int main() {
Init(MAXN);
scanf("%d", &Q);
for (int i = 1; i <= Q; i++)
scanf("%d%d%d", &P[i].N, &P[i].M, &P[i].A), P[i].i = i;
std::sort(P + 1, P + Q + 1, Comp);
int cur = 0;
for (int i = 1; i <= Q; i++) {
int lim = P[i].A;
while (cur < (int)Sig.size() && Sig[cur].first <= lim) {
int d = Sig[cur].second, s = Sig[cur].first;
for (int j = d; j <= MAXN; j += d)
T.Update(j, s * Mu[j / d]);
cur++;
}
Ans[P[i].i] = Calc(P[i].N, P[i].M);
}
for (int i = 1; i <= Q; i++)
printf("%d\n", Ans[i] & 2147483647);
return 0;
}