1. 程式人生 > 實用技巧 >洛谷 P3327

洛谷 P3327

題目連結:P3327 [SDOI2015]約數個數和

題目大意

求約數個數和

solution

我們知道有這麼一個公式: \(d(nm) = \sum\limits_{i | n}\sum\limits_{j | m}[gcd(i, j) == 1]\)

證明: 對於 \(nm\) 的每個質因數 \(p\), 設 \(n = n_1 * p^a, m = m_1 * p^b\), 那麼 \(nm = n_1 * m_1 * p^{a + b}\), p 對 d(nm) 的貢獻是 \(a+b+1\).在等式的右邊裡,\((i_1 * p^a, j_1), (i _ 1 * p^{a - 1}, j_1), ..., (i_1, j_1), ..., (i_1, j_1 * p^b)\)

都是可行的數對, 共 \(a + b + 1\) 個, 證畢.

那麼:

\(\sum\limits_{i = 1}^N\sum\limits_{j = 1}^M d(i, j)\)

標準的展開:

$= \sum\limits_{i = 1}^N \sum\limits_{j = 1}^{M} \sum\limits_{k | i} \sum\limits_{l | j} [gcd(k, l) == 1] $

$=\sum\limits_{i = 1}^N \sum\limits_{j = 1}^M\sum\limits_{d = 1}^{min(N, M)}\mu(d)\sum\limits_{k|i}\sum\limits_{l|j}[gcd(k, l)|d] $

交換列舉順序:

\(=\sum\limits_{d = 1}^{min(N, M)}\mu(d)\sum\limits_{k|i}\sum\limits_{l|j}[gcd(k, l)|d] \left\lfloor\frac{N}{k}\right\rfloor\left\lfloor\frac{M}{l}\right\rfloor\)

$=\sum\limits_{d = 1}^{min(N, M)}\mu(d)\sum\limits_{k}^{\left\lfloor\frac{N}{k}\right\rfloor}\sum\limits_{l}^{\left\lfloor\frac{M}{l}\right\rfloor}\left\lfloor\frac{N}{dk}\right\rfloor\left\lfloor\frac{M}{dl}\right\rfloor $

交換列舉順序:

$=\sum\limits_{d = 1}^{min(N, M)}\mu(d)\sum\limits_{k}^{\left\lfloor\frac{N}{k}\right\rfloor}\left\lfloor\frac{N}{dk}\right\rfloor\sum\limits_{l}^{\left\lfloor\frac{M}{l}\right\rfloor}\left\lfloor\frac{M}{dl}\right\rfloor $

\(f(i) = \sum\limits_{j = 1}^i \left\lfloor\frac{i}{j}\right\rfloor\), 則我們要求 $=\sum\limits_{i = 1}^{min(N, M)}\mu(i) * f(\left\lfloor\frac{N}{i}\right\rfloor) * f(\left\lfloor\frac{M}{i}\right\rfloor) $

然後用一下數論分塊我們這道題就做完了

Code:

/**
*    Author: Alieme
*    Data: 
*    Problem: 
*    Time: O()
*/
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <algorithm>

#define int long long
#define rr register

#define inf 1e9
#define MAXN 100010

using namespace std;

inline int read() {
	int s = 0, f = 0;
	char ch = getchar();
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
	return f ? -s : s;
}

void print(int x) {
	if (x < 0) putchar('-'), x = -x;
	if (x > 9) print(x / 10);
	putchar(x % 10 + 48);
}

int T, n, m, ans, tot;

int mu[MAXN], d[MAXN], c[MAXN], prime[MAXN];

bool vis[MAXN];

inline void init() {
	mu[1] = d[1] = c[1] = 1;
	for (rr int i = 2; i <= 50000; i++) {
		if (!vis[i]) prime[++tot] = i, mu[i] = -1, c[i] = 1, d[i] = 2;
		for (int j = 1; j <= tot; j++) {
			if (i * prime[j] > 50000) break;
			vis[i * prime[j]] =  1;
			mu[i * prime[j]] = -mu[i];
			d[i * prime[j]] = d[i] * d[prime[j]];
			c[i * prime[j]] = 1;
			if (i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				d[i * prime[j]] = d[i] / (c[i] + 1) * (c[i] + 2);
				c[i * prime[j]] = c[i] + 1;
				break;
			}
		}
	}
	for (rr int i = 1; i <= 50000; i++) mu[i] += mu[i - 1];
	for (rr int i = 1; i <= 50000; i++) d[i] += d[i - 1];		
}

signed main() {
	init();
	T = read();
	while (T--) {
		n = read();
		m = read();
		ans = 0;
		for (rr int l = 1, r; l <= min(n, m); l = r + 1) {
			r = min(n / (n / l), m / (m / l));
			ans += (mu[r] - mu[l - 1]) * d[n / l] * d[m / l];
		}
		print(ans);
		puts("");
	}
}