P3312 学习笔记
数表里第 \(i\) 行第 \(j\) 列的数相当于
\[\sigma(\gcd(i,j))
\]
我们先不管 \(a\),那么我们要求的就是
\[\sum_{i=1}^n \sum_{j=1}^m \sigma(\gcd(i,j))
\]
直接枚举因数并交换求和顺序
\[\sum_{d=1}^n \sum_{i=1}^{\lfloor n/d \rfloor} \sum_{j=1}^{\lfloor m/d \rfloor} \sigma(d)[\gcd(i,j)=1]
\]
把 \(\sigma(d)\) 提到前面
\[\sum_{d=1}^n \sigma(d) \sum_{i=1}^{\lfloor n/d \rfloor} \sum_{j=1}^{\lfloor m/d \rfloor} [\gcd(i,j)=1]
\]
把 \([\gcd(i,j)=1]\) 用莫比乌斯函数表示,即
\[\sum_{d=1}^n \sigma(d) \sum_{i=1}^{\lfloor n/d \rfloor} \sum_{j=1}^{\lfloor m/d \rfloor} \sum_{e \mid i,e \mid j} \mu(e)
\]
我们把 \(e\) 提前
\[\sum_{d=1}^n \sigma(d) \sum_{e=1}^{\lfloor n/d \rfloor} \mu(e) \lfloor \frac n{de} \rfloor \lfloor \frac m{de} \rfloor
\]
我们注意到 \(de\) 这一个整体重复出现两次,考虑直接枚举
\[\sum_{t=1}^n \lfloor \frac nt \rfloor \lfloor \frac mt \rfloor \sum_{d \mid t} \sigma(d) \mu(\frac td)
\]
令
\[f(x)=\sum_{d \mid x} \sigma(d) \mu(\frac xd)
\]
那么考虑 \(a\) 的限制,当且仅当 \(\sigma(d) \le a\) 时才会对 \(f(x)\) 的计算有贡献,离线将所有的 \(a\) 排序处理即可。
code
#include <bits/stdc++.h>
#define fast std::ios::sync_with_stdio(false); std::cin.tie(NULL); std::cout.tie(NULL);
constexpr std::int32_t mod = 998244353;
constexpr std::int32_t maxn = 1e5 + 5;
constexpr std::int32_t maxm = 2e4 + 5;
std::int32_t mu[maxn], prime[maxn], g[maxn], Binary_Indexed_Tree[maxn], tot, ans[maxm], t;
bool vis[maxn];
std::pair <std::int32_t, std::int32_t> f[maxn];
struct Query{
std::int32_t n, m, a, idx;
bool operator < (const Query &other) const {
return a < other.a;
}
} querys[maxm];
void sieve(){
mu[1] = 1;
f[1] = std::make_pair(1, 1);
for (std::int32_t i = 2; i <= maxn - 5; i++){
if (!vis[i]) prime[++tot] = i, mu[i] = -1, g[i] = i + 1, f[i] = std::make_pair(i + 1, i);
for (std::int32_t j = 1; j <= tot && i * prime[j] <= maxn - 5; j++){
vis[i * prime[j]] = true;
if (i % prime[j] == 0){
mu[i * prime[j]] = 0;
g[i * prime[j]] = g[i] * prime[j] + 1;
f[i * prime[j]] = std::make_pair(f[i].first / g[i] * g[i * prime[j]], i * prime[j]);
break;
} else {
mu[i * prime[j]] = -mu[i];
f[i * prime[j]] = std::make_pair(f[i].first * f[prime[j]].first, i * prime[j]);
g[i * prime[j]] = prime[j] + 1;
}
}
}
std::sort(f + 1, f + maxn - 5 + 1);
}
std::int32_t lowbit(std::int32_t i){ return i & (-i); }
void update(std::int32_t x, std::int32_t y){ for (std::int32_t i = x; i <= maxn - 5; i += lowbit(i)) Binary_Indexed_Tree[i] += y; }
std::int32_t query(std::int32_t x){ std::int32_t res = 0; for (std::int32_t i = x; i; i -= lowbit(i)) res += Binary_Indexed_Tree[i]; return res; }
std::int32_t solve(std::int32_t n, std::int32_t m){
if (n > m) std::swap(n, m); std::int32_t res = 0;
for (std::int32_t pl = 1, pr; pl <= n; pl = pr + 1){
pr = std::min(n / (n / pl), m / (m / pl));
res += (query(pr) - query(pl - 1)) * (n / pl) * (m / pl);
}
return res;
}
int main() {
std::freopen("contest.in", "r", stdin);
std::freopen("contest.out", "w", stdout);
fast;
sieve();
std::cin >> t;
for (std::int32_t i = 1; i <= t; i++) std::cin >> querys[i].n >> querys[i].m >> querys[i].a, querys[i].idx = i;
std::sort (querys + 1, querys + t + 1);
for (std::int32_t i = 1, j = 1; i <= t; i++){
while (f[j].first <= querys[i].a && j <= maxn - 5){
for (std::int32_t k = f[j].second; k <= maxn - 5; k += f[j].second) update(k, f[j].first * mu[k / f[j].second]);
j++;
}
ans[querys[i].idx] = solve(querys[i].n, querys[i].m);
}
for (std::int32_t i = 1; i <= t; i++) std::cout << (ans[i] & (~(1 << 31))) << std::endl;
return 0;
}

浙公网安备 33010602011771号