【筆記】積性函數、莫比烏斯反演


time: 2021-11-05 21:00:22
tags: 
- 筆記 聽課筆記 數論/積性函數 數論/莫比烏斯反演

數論函數

定義域為正整數,陪域為實數的函數。

積性函數

定義當 \((a,b)=1\) 時滿足 \(f(ab)=f(a)f(b)\) 的函數為積性函數。而對於任意 \(a,b\)\(f(ab)=f(a)f(b)\) 都成立的函數叫做完全積性函數。

常見的積性函數有

  • 恆等函數 \(I(n)=1\)
  • 冪函數 \(I_k(n)=n^k\)
  • 單位函數 \(id(n)=n\)
  • 元函數 \(\varepsilon (n)=\begin{cases}1,&n=1\\0,&n>1\end{cases}\)
  • 因子和函數 \(\sigma(n)=\sum_{d\mid n}d\)
  • 約數個數函數 \(d(n)=\sum_{d\mid n} 1\)

【例 關於積性】

如設 \(\sigma_k(n)=\sum_{d\mid n}d^k\),則 \(\sigma_k(n)\) 也是積性函數。考慮設互質的兩個數 \(a=\prod_{i=1}^x p_i^{q_i},b=\prod_{i=1}^yh_i^{s_i}\).

那么 \(\sigma_k(a)=\sum_{i=1}^x(1+p_i+\dots+p_i^{q_i})^k\)\(\sigma_k(b)=\sum_{i=1}^y(1+h_i+\dots+h_i^{s_i})^k\),所以顯然有 \(\sigma_k(ab)=\sigma_k(a)\sigma_k(b)\).

線性篩積性函數

線性篩任何積性函數都要分兩種情況考慮, 一是當前數 \(i\bmod p_j\ne0\),二是 \(i\bmod p_j=0\). \(i\bmod p_j\ne 0\) 的情況意味着 \((i,p_j)=1\),則 \(f(ip_j)=f(i)f(p_j)\) 即可。\(i\bmod p_j=0\) 的情況意味着 \(p_j\)\(i\) 最小的質因數。設 \(i=p_j^q\times k\),則我們可以記下 \(q\) 的值,如果能 \(O(1)\) 直接計算 \(f(p_j^{q+1})\),或者 \(O(1)\)\(f(p_j^q)\) 轉移得到 \(f(p_j^{q+1})\) 的值,就可以算出 \(f(ip_j)=f(i)/f(p_j^q)\times f(p_j^{q+1})\) 或者 \(f(ip_j)=f(i/p_j^q)\times f(p_j^{q+1})\),不管怎么算,都需要記下對於 \(i\) 來說 \(f(p_j^q)\) 的值。

不過對於一些特殊的積性函數,有特殊的計算技巧使得我們不用考慮那么多東西。

\(\varphi\)

對於 \(p\mid i\) 的情況,有 \(\varphi(pi)=pi\prod_{j=1}^q\frac{s_i-1}{s_i}=p\times \varphi(i)\).
什么額外的東西都不用記。

\(\mu\)

對於 \(p\mid i\) 的情況,發現 \(p\) 的冪次大於了 1,因此 \(\mu(pi)=0\).
什么額外的東西都不用記。

\(d\)(約數個數)

對於 \(p\mid i\) 的情況,則 \(d(pi)=d(i)/d(p^q)\times (p^{q+1})=d(i)/(q+1)\times(q+2)\).
需要記下最小質因子的次數 \(q\)

\(\sigma\)(約數和)

對於 \(p\mid i\) 的情況,\(\sigma(pi)=\sigma(i)/\sigma(p^q)\times \sigma (p^{q+1})=\sigma(i)/\sigma(p^q)\times (\sigma(p^q)+p^{q+1})\).

需要記下 \(q\)\(p^q\).

代碼

什么?你說 \(\sigma\) 會爆 int?這與我有什么關系

#include <bits/stdc++.h>

using namespace std;
const int N = 1e7 + 5;
int flag[N], phi[N], mu[N], d[N], sig[N];
int q[N], pq[N], p[N], v, c;
void sieve(int n) {
	flag[1] = true, phi[1] = 1, mu[1] = 1, d[1] = 1, sig[1] = 1;
	for(int i = 2; i <= n; i++) {
		if(!flag[i]) {
			p[++c] = i, phi[i] = i - 1, mu[i] = -1, d[i] = 2, sig[i] = 1 + i;
			q[i] = 1, pq[i] = i;
		}
		for(int j = 1; j <= c && (v = i * p[j]) <= n; j++) {
			flag[v] = true;
			if(i % p[j]) {
				phi[v] = phi[p[j]] * phi[i];
				mu[v] = -mu[i]; // mu[v] = mu[p[j]] * mu[i]
				d[v] = d[p[j]] * d[i];
				sig[v] = sig[p[j]] * sig[i];
				q[v] = 1, pq[v] = p[j];
			} else {
				phi[v] = p[j] * phi[i];
				mu[v] = 0;
				d[v] = d[i] / (q[i] + 1) * (q[i] + 2);
				sig[v] = sig[i] / sig[pq[i]] * (sig[pq[i]] + pq[i] * p[j]);
				q[v] = q[i] + 1, pq[v] = pq[i] * p[j];
				break;
			}
		}
	}
}

int main() {
	sieve(1e7);
	for(int i = 1; i <= 20; i++) {
		debug(i, phi[i], mu[i], d[i], sig[i], q[i], pq[i]);
	}

	return 0;
}

狄利克雷卷積

定義兩個數論函數 \(f,g\) 的狄利克雷卷積 \(f\ast g=\sum_{d\mid n}f(d)g(\frac nd)\). 狄利克雷卷積具有交換律和結合律。兩個積性函數在狄利克雷卷積后得到的函數仍然是積性函數。

\(\varphi\ast I=id\)

\(\sum_{d\mid n}\varphi(d)=n\). 考慮兩個集合,\(S_1=\{(i,n)|1\le i\le n\}\)\(S_2=\{(i,p)|1\le i\le p\le n,\gcd(i,p)=1,p\mid n\}\). 將 \(S_1\) 中的元素視作分數 \(\frac in\) 后構成分數集合 \(P_1\). 這些分數約分后一定在 \(S_2\) 構成的分數集合 \(P_2\) 中,所以 \(|S_1|\le|S_2|\).

而將 \(P_2\) 中的分數 \(\frac ip\) 化為 \(\frac {ig}n\),其中 \(g=\frac np\) 后,由於 \(\frac ip\) 的值兩兩不同,所以 \(ig\) 也兩兩不同。而 \(1\le ig\le n\),所以 \(P_2\) 中的元素也都在 \(P_1\) 中。

綜上,\(|S_1|=|S_2|\),即 \(n=\sum_{d\mid n}\varphi(d)\).

\(\mu\ast I=\varepsilon\)

\(n>1\)

\[\begin{align*}\mu\ast id&=\sum_{d\mid n}\mu(d)=\sum_{i=1}^t(-1)^i\binom ti+\mu(1)=\sum_{i=0}^t(-1)^i\binom ti\\&=(1-1)^t=0.\end{align*} \]

\(n=1\) 時顯然有 \(\mu\ast I=1\).

\(\mu\ast id=\varphi\)

第一種證法:

\[\mu\ast I=\varepsilon \]

\[\mu\ast I\ast id=id \]

\[\mu\ast I\ast id=\varphi\ast I \]

\[\mu\ast id=\varphi \]

第二種證法:

考慮容斥計算 \(\varphi\),比如 \(\varphi(60)=60-30-20-12+10+6+4-2=16\). 即有 0 個公共質因數的數的個數等於至少有 0 個的,減去至少有一個的,加上至少有兩個的 ...

而右邊的部分就是 \(\mu\ast id\).

整除分塊(數論分塊)

\(\sum_{i=1}^n\lfloor\frac ni\rfloor\),如果直接枚舉是 \(O(n)\) 的,使用整除分塊的方法就是 \(O(\sqrt n)\).

考慮 \(\lfloor\frac ni\rfloor\)\(i\le \sqrt n\) 的時候至多有 \(\sqrt n\) 種不同的取值,而在 \(i>\sqrt n\) 時,\(\lfloor\frac ni\rfloor< \sqrt n\),至多有 \(\sqrt n\) 種不同的取值。因此 \(\lfloor\frac ni\rfloor\) 最多有 \(2\sqrt n\) 種不同的取值。

在算法實現時,先枚舉 i=1 的情況,此時 \(\lfloor \frac ni\rfloor =n\),我們試圖 \(O(1)\) 算出 \(\lfloor \frac ni\rfloor\) 同樣等於 \(n\) 的數的右端點。

\(\lfloor\frac ni\rfloor=k\) 實際上意味着 \(ki+p=n,0\le p<i\),而如果 \(\lfloor\frac n{i+d}\rfloor=k\),同樣有 \(k(i+d)+p'=n,0\le p<i+d\). 於是有 \(p'=p-kd\),所以 \(d\le\frac pk\)\(d_{max}=\lfloor\frac pk\rfloor\).

所以

\[\begin{align*} i_{max}&=i+d_{max}\\ &=i+\left\lfloor\frac pk\right\rfloor\\ &=\left\lfloor i+\frac pk\right\rfloor\\ &=\left\lfloor \frac {ki+p}k\right\rfloor\\ &=\left\lfloor\frac{n}{\lfloor\frac ni\rfloor}\right\rfloor \end{align*} \]

在算出這個右端點 \(i_{max}\) 后,下一個左端點就等於 \(i_{max}+1\).

莫比烏斯反演定理

【定理 莫比烏斯反演定理 形式 1】

\[F(n)=\sum_{d\mid n}f(d) \]

則有

\[f(n)=\sum_{d\mid n}F(d)\mu\left(\frac nd\right) \]

【證明】

\[F=f\ast I \]

\[F\ast \mu=f\ast I\ast \mu \]

\[f=F\ast \mu \]

【定理 莫比烏斯反演定理 形式 2】

這個其實在做題的時候更常用。

\[F(n)=\sum_{n\mid d}f(d) \]

\[f(n)=\sum_{n\mid d}F(d)\mu(\frac dn) \]

【證明】

![[files/Pasted image 20211110172030.png]]

莫比烏斯反演入門題

P2158 [SDOI2008] 儀仗隊

作為體育委員,C 君負責這次運動會儀仗隊的訓練。儀仗隊是由學生組成的 \(N \times N\) 的方陣,為了保證隊伍在行進中整齊划一,C 君會跟在儀仗隊的左后方,根據其視線所及的學生人數來判斷隊伍是否整齊(如下圖)。

現在,C 君希望你告訴他隊伍整齊時能看到的學生人數。

顯然答案為 \(2\sum_{i=1}^{n-1}\varphi(i)+2\),但是可以用一點高妙的方法做。

答案為

\[\begin{align*} &\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}[\gcd(i,j)=1]+2\\ =&\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}\varepsilon(\gcd(i,j))+2\\ =&\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}\mu\ast I(\gcd(i,j))+2\\ =&\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}\sum_{d\mid \gcd(i,j)}\mu(d)+2\\ =&\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}\sum_{d=1}^{n-1}\mu(d)[d\mid i\wedge d\mid j]+2\\ =&\sum_{d=1}^{n-1}\mu(d)\left\lfloor\frac {n-1}d\right\rfloor^2+2 \end{align*} \]

\([\gcd(i,j)=1]\) 轉化為 \(\sum_{d\mid \gcd(i,j)}\mu(d)\) 是個常用技巧,后面也會用到。有些題解說這個就是莫比烏斯反演??

注意 \(n=1\) 的時候答案為 0。。。

#include <bits/stdc++.h>

using namespace std;
const int N = 40005;
int n;
int flag[N], phi[N], mu[N], d[N], sig[N];
int q[N], pq[N], p[N], v, c;
void sieve(int n) {
	flag[1] = true, phi[1] = 1, mu[1] = 1, d[1] = 1, sig[1] = 1;
	for(int i = 2; i <= n; i++) {
		if(!flag[i]) {
			p[++c] = i, phi[i] = i - 1, mu[i] = -1, d[i] = 2, sig[i] = 1 + i;
			q[i] = 1, pq[i] = i;
		}
		for(int j = 1; j <= c && (v = i * p[j]) <= n; j++) {
			flag[v] = true;
			if(i % p[j]) {
				phi[v] = phi[p[j]] * phi[i];
				mu[v] = -mu[i]; // mu[v] = mu[p[j]] * mu[i]
				d[v] = d[p[j]] * d[i];
				sig[v] = sig[p[j]] * sig[i];
				q[v] = 1, pq[v] = p[j];
			} else {
				phi[v] = p[j] * phi[i];
				mu[v] = 0;
				d[v] = d[i] / (q[i] + 1) * (q[i] + 2);
				sig[v] = sig[i] / sig[pq[i]] * (sig[pq[i]] + pq[i] * p[j]);
				q[v] = q[i] + 1, pq[v] = pq[i] * p[j];
				break;
			}
		}
	}
}

int main() {
	cin >> n;
	sieve(n);
	long long ans = 0;
	for(int i = 1; i <= n - 1; i++)
		ans += mu[i] * (long long)((n - 1) / i) * ((n - 1) / i);
	if(n > 1) ans += 2;
	cout << ans << '\n';

	return 0;
}

P1829 [國家集訓隊]Crash的數字表格 / JZPTAB

今天的數學課上,Crash 小朋友學習了最小公倍數(Least Common Multiple)。對於兩個正整數 \(a\)\(b\)\(\text{lcm}(a,b)\) 表示能同時整除 \(a\)\(b\) 的最小正整數。例如,\(\text{lcm}(6, 8) = 24\)

回到家后,Crash 還在想着課上學的東西,為了研究最小公倍數,他畫了一張 \(n \times m\) 的表格。每個格子里寫了一個數字,其中第 \(i\) 行第 \(j\) 列的那個格子里寫着數為 \(\text{lcm}(i, j)\)

看着這個表格,Crash 想到了很多可以思考的問題。不過他最想解決的問題卻是一個十分簡單的問題:這個表格中所有數的和是多少。當 \(n\)\(m\) 很大時,Crash 就束手無策了,因此他找到了聰明的你用程序幫他解決這個問題。由於最終結果可能會很大,Crash 只想知道表格里所有數的和 \(\bmod 20101009\) 的值。

\(n\le m\),答案為

\[\begin{align*} &\sum_{i=1}^n\sum_{j=1}^m \operatorname{lcm}(i,j)\\ =&\sum_{i=1}^n\sum_{j=1}^m\frac{ij}{\gcd(i,j)}\\ =&\sum_{i=1}^n\sum_{j=1}^m\sum_{d=1}^n\frac{ij}{d}[\gcd(i,j)=d] \end{align*} \]

現在將枚舉 \(i,j\) 換為枚舉 \(d\) 的倍數,得到

\[\begin{align*} &\sum_{d=1}^n\sum_{i=1}^{\lfloor n/d\rfloor}\sum_{j=1}^{\lfloor m/d\rfloor}dij[\gcd(i,j)=1]\\ =&\sum_{d=1}^n\sum_{i=1}^{\lfloor n/d\rfloor}\sum_{j=1}^{\lfloor m/d\rfloor}dij\sum_{l\mid \gcd(i,j)}\mu(l)\\ =&\sum_{d=1}^nd\sum_{l=1}^{\lfloor n/d\rfloor}\sum_{i=1}^{\lfloor n/dl\rfloor}\sum_{j=1}^{\lfloor m/dl\rfloor}ijl^2\mu(l)\\ =&\sum_{d=1}^nd\sum_{l=1}^{\lfloor n/d\rfloor}l^2\mu(l)\sum_{i=1}^{\lfloor n/dl\rfloor}i\sum_{j=1}^{\lfloor m/dl\rfloor}j \end{align*} \]

\(n/d\) 可以整除分塊一下,乘上一個 \(\sqrt n\),然后 \(n/dl\) 又可以整除分塊一下,所以總復雜度是 \(O(n)\).

代碼:

記得把減法的地方都加上 MD.

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 1e7 + 5, MD = 20101009;
int n, m;

int flag[N], mu[N], f[N], S[N], p[N], c;
void init(int n) {
	flag[1] = true, mu[1] = 1, f[1] = 1, S[1] = 1;
	for(int i = 2, v; i <= n; i++) {
		if(!flag[i]) p[++c] = i, mu[i] = -1 + MD;
		for(int j = 1; j <= c && (v = i * p[j]) <= n; j++) {
			flag[v] = true;
			if(i % p[j]) mu[v] = (-mu[i] + MD) % MD;
			else {
				mu[v] = 0;
				break;
			}
		}
		f[i] = (f[i - 1] + ((ll)i * i) % MD * mu[i] % MD) % MD;
		S[i] = (S[i - 1] + i) % MD;
	}
}

int sum(int x, int y) {
	int res = 0;
	for(int i = 1, j; i <= x; i = j + 1) {
		j = min(x / (x / i), y / (y / i));
		res += (ll)(f[j] - f[i - 1] + MD) * S[x / i] % MD * S[y / i] % MD;
		res %= MD;
	}
	return res;
}

int calc(int n, int m) {
	int res = 0;
	for(int i = 1, j; i <= n; i = j + 1) {
		j = min(n / (n / i), m / (m / i));
		res += (ll)(S[j] - S[i - 1] + MD) * sum(n / i, m / i) % MD;
		res %= MD;
	}
	return res;
}

int main() {
	cin >> n >> m;
	if(n > m) swap(n, m);
	init(m);
	cout << calc(n, m) << '\n';

	return 0;
}

BZOJ2694 lcm

\[\begin{align*} &\sum_{i=1}^A\sum_{j=1}^B\mu^2(\gcd(i,j))\frac{ij}{\gcd(i,j)}\\ =&\sum_{d=1}^{\min(A,B)}\sum_{i=1}^A\sum_{j=1}^B\mu^2(d)\frac{ij}d[\gcd(i,j)=d]\\ =&\sum_{d=1}^{\min(A,B)}d\sum_{i=1}^{\lfloor A/d\rfloor}\sum_{j=1}^{\lfloor B/d\rfloor}\mu^2(d)ij[\gcd(i,j)=1]\\ \end{align*} \]

注意這一步相當於把 \(di,dj\) 換成了 \(i,j\),相應的在后面就要乘上 \(d^2\). 然后用上面的技巧 \([\gcd(i,j)=1]=\sum_{l\mid\gcd(i,j)}\mu(l)\) 得到

\[\begin{align*} &\sum_{d=1}^{\min(A,B)}d\mu^2(d)\sum_{i=1}^{\lfloor A/d\rfloor}\sum_{j=1}^{\lfloor B/d\rfloor}ij\sum_{l\mid \gcd(i,j)}\mu(l)\\ =&\sum_{d=1}^{\min(A,B)}d\mu^2(d)\sum_{i=1}^{\lfloor A/dl\rfloor}i\sum_{j=1}^{\lfloor B/dl\rfloor}j\sum_{l=1}^{\min(A,B)/d}\mu(l)l^2 \end{align*} \]

\(T=dl,F(i)=\frac{x(x+1)}2\),得到

\[\sum_{d=1}^{\min(A,B)}d\mu^2(d)\sum_{l=1}^{\min(A,B)/d}\mu(l)l^2F\left(\frac AT\right)F\left(\frac BT\right) \]

如果此時先枚舉 \(T\),再枚舉 \(d\),那么 \(l\) 就可以直接確定,即 \(\frac Td\). 寫出來就是

\[\sum_{T=1}^{\min(A,B)}F\left(\frac AT\right)F\left(\frac BT\right)\sum_{d\mid T}d\mu^2(d)\mu\left(\frac Td\right)\left(\frac Td\right)^2 \]

針對 \(F(A/T)F(B/T)\) 的不同取值,可以整除分塊。后面的東西是個積性函數,但是線性篩不好篩,可以枚舉 \(d\),然后對 \(d\) 的倍數計算貢獻。復雜度是 \(n(1+\frac 12+\frac 13+\dots +\frac 1n)=O(n\log n)\).

代碼:

模數是 \(2^{30}\),可以直接利用 unsigned int 類型的自動溢出。

#include <bits/stdc++.h>
#define int unsigned int

using namespace std;
const int N = 4e6 + 5;
int T, n, m;

int flag[N], f[N], mu[N], p[N], S[N], v, c;
void sieve1(int n) { // 線性篩 mu
	mu[1] = 1;
	for(int i = 2; i <= n; i++) {
		if(!flag[i]) mu[i] = -1, p[++c] = i;
		for(int j = 1; j <= n && (v = p[j] * i) <= n; j++) {
			flag[v] = true;
			if(i % p[j]) mu[v] = -mu[i];
			else {
				mu[v] = 0;
				break;
			}
		}
	}
}
void sieve2(int n) { // n log n 篩 f
	S[1] = 1;
	for(int i = 1; i <= n; i++) {
		for(int j = i; j <= n; j += i) {
			f[j] += i * mu[i] * mu[i] * mu[j / i] * (j / i) * (j / i);
		}
		S[i] = S[i - 1] + i;
	}
	for(int i = 1; i <= n; i++) f[i] += f[i - 1]; // 對 f 做前綴和,方便整除分塊
}

int calc(int n, int m) {
	int res = 0;
	for(int i = 1, j; i <= n; i = j + 1) {
		j = min(n / (n / i), m / (m / i));
		res += (f[j] - f[i - 1]) * S[n / i] * S[m / i];
	}
	return res;
}

signed main() {
	sieve1(4e6);
	sieve2(4e6);
	for(cin >> T; T; T--) {
		cin >> n >> m;
		if(n > m) swap(n, m);
		cout << calc(n, m) % (1 << 30) << '\n';
	}

	return 0;
}

P3327 [SDOI2015]約數個數和

\(d(x)\)\(x\) 的約數個數,給定 \(n,m\),求

\[\sum_{i=1}^n\sum_{j=1}^md(ij) \]

\(T,n,m\le 50000\)


【結論】

\[d(ij)=\sum_{x\mid i}\sum_{y\mid j}[\gcd(x,y)=1] \]

【證明】\(ij\) 的一個質因子 \(p\) 的冪次為 \(c\),那么 \(d(ij)\) 就是所有 \(c+1\) 相乘,意義是從 \(p^0,p^1,\dots,p^c\) 里面選出來一個。

那么設 \(i\) 的質因子 \(p\) 的冪次為 \(a\)\(j\) 的質因子的冪次為 \(b\)。把「在 \(i\) 中選擇 \(p^x\)」視作「在 \(ij\) 中選擇 \(p^x\)」,把「在 \(j\) 中選擇 \(p^y\)」視作「在 \(ij\) 中選擇 \(p^{a+x}\)」. 這樣就可以組合出 \(ij\) 所有的約數,而條件是要求 \(i\) 的約數和 \(j\) 的約數互質。


【結論】

\[\left\lfloor\frac {\left\lfloor\frac xA\right\rfloor}{B}\right\rfloor=\left\lfloor\frac x{AB}\right\rfloor \]

【證明】

\(x=Ai+p,\ (0\le p<A)\),則

\[\left\lfloor\frac {\left\lfloor\frac xA\right\rfloor}{B}\right\rfloor=\left\lfloor\frac iB\right\rfloor, \]

\[\left\lfloor\frac x{AB}\right\rfloor=\left\lfloor\frac{Ai+p}{AB}\right\rfloor=\left\lfloor\frac{i+\frac pA}B\right\rfloor. \]

由於 \(\frac pA<1\),所以

\[\left\lfloor\frac iB\right\rfloor=\left\lfloor\frac {i+\frac pA}B\right\rfloor, \]

原式得證。


現在開始做題了

\[\begin{align*} &\sum_{i=1}^n\sum_{j=1}^m d(ij)\\ =&\sum_{i=1}^n\sum_{j=1}^m\sum_{x\mid i}\sum_{y\mid j}[\gcd(x,y)=1]\tag1 \end{align*} \]

然后這一步我直接用莫比烏斯函數 \(\sum_{d\mid \gcd(x,y)}\) 替換了 \([\gcd(x,y)=1]\),弄出來

\[\begin{align*} &\sum_{i=1}^n\sum_{j=1}^m\sum_{x\mid i}\sum_{y\mid j}\sum_{d\mid \gcd(x,y)}\mu(d)\\ =&\sum_{d=1}^n\sum_{i=1}^n\sum_{x\mid i}\lfloor\frac xd\rfloor\sum_{j=1}^m\sum_{y\mid j}\lfloor\frac yd\rfloor \end{align*} \]

孩子懵逼了,雖然可以算,但是復雜度不對。

回到式 (1),其實可以用一個技巧化簡,把其中兩個求和號搞掉。

\[\begin{align*} &\sum_{i=1}^n\sum_{j=1}^m\sum_{x=1}^n\sum_{y=1}^m[\gcd(x,y)=1][x\mid i][y\mid j]\\ =&\sum_{x=1}^n\sum_{y=1}^m\sum_{i=1}^n\sum_{j=1}^m[x\mid i][y\mid j][\gcd(x,y)=1]\\ =&\sum_{x=1}^n\sum_{j=1}^m\left\lfloor\frac nx\right\rfloor\left\lfloor\frac my\right\rfloor[\gcd(x,y)=1]\\ =&\sum_{x=1}^n\sum_{y=1}^m\sum_{d\mid \gcd(x,y)}\left\lfloor\frac nx\right\rfloor\left\lfloor\frac my\right\rfloor\mu(d)\\ \end{align*} \]

又出現了式 (1) 中求和號底下有個整除條件的形式!於是可以確定枚舉的東西的上下界,然后把條件丟到要求得東西里去。最后改變一下枚舉的東西,就可以變得更簡單。

這個技巧一直都在用,只是這里歸納一下。

\[\begin{align*} \sum_{d=1}^n\sum_{x=1}^n\sum_{y=1}^m[d\mid x][d\mid y]\left\lfloor\frac nx\right\rfloor\left\lfloor\frac my\right\rfloor\mu(d) \end{align*} \]

把枚舉 \(x,y\) 改成枚舉 \(dx,dy\),得到

\[\begin{align*} &\sum_{d=1}^n\mu(d)\sum_{x=1}^{\lfloor n/d\rfloor}\sum_{y=1}^{\lfloor m/d\rfloor}\left\lfloor\frac n{dx}\right\rfloor\left\lfloor\frac m{dy}\right\rfloor\\ =&\sum_{d=1}^n\mu(d)\sum_{x=1}^{\lfloor n/d\rfloor}\sum_{y=1}^{\lfloor m/d\rfloor}\left\lfloor\frac {\lfloor\frac nd\rfloor}{x}\right\rfloor\left\lfloor\frac {\lfloor\frac md\rfloor}{y}\right\rfloor \end{align*} \]

\(S(x)=\sum_{i=1}^x\lfloor\frac xi\rfloor\),考慮「枚舉每個數的倍數,做出 1 的貢獻」就等於「所有數的約數個數和」,有

\[\sum_{i=1}^x\lfloor\frac xi\rfloor=\sum_{i=1}^x d(i), \]

所以 \(S(x)\) 可以 \(O(n)\) 線性篩。最后答案為

\[\sum_{d=1}^n\mu(d)S(\lfloor n/d\rfloor)S(\lfloor m/d\rfloor). \]

#include <bits/stdc++.h>
#define int long long

using namespace std;
const int N = 5e4 + 5;
int T, n, m;

int flag[N], p[N], mu[N], d[N], q[N], S[N], Smu[N], c;
void sieve(int n) {
	flag[1] = true, mu[1] = 1, d[1] = 1, S[1] = 1, Smu[1] = 1;
	for(int i = 2, v; i <= n; i++) {
		if(!flag[i]) p[++c] = i, mu[i] = -1, d[i] = 2, q[i] = 1;
		for(int j = 1; j <= c && (v = i * p[j]) <= n; j++) {
			flag[v] = true;
			if(i % p[j]) {
				mu[v] = -mu[i];
				d[v] = d[i] * d[p[j]];
				q[v] = 1;
			} else {
				mu[v] = 0;
				d[v] = d[i] / (q[i] + 1) * (q[i] + 2);
				q[v] = q[i] + 1;
				break;
			}
		}
		S[i] = S[i - 1] + d[i];
		Smu[i] = Smu[i - 1] + mu[i];
	}
}

int calc(int n, int m) {
	int res = 0;
	for(int i = 1, j; i <= n; i = j + 1) {
		j = min(n / (n / i), m / (m / i));
		res += (Smu[j] - Smu[i - 1]) * S[n / i] * S[m / i];
	}
	return res;
}

signed main() {
	ios::sync_with_stdio(false); cin.tie(nullptr);
	sieve(5e4);
	for(cin >> T; T; T--) {
		cin >> n >> m;
		if(n > m) swap(n, m);
		cout << calc(n, m) << '\n';
	}

	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM