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;
}
posted @ 2026-04-18 08:05  constexpr_ll  阅读(5)  评论(0)    收藏  举报