学习笔记 ---- 莫比乌斯反演总结

概述

对于一些函数 f ( n ) f(n) f(n),如果很难直接求出它的值,但是容易求出其约数或倍数的 g ( n ) g(n) g(n)和,并且 f f f g g g 满足一定关系。那么可以通过莫比乌斯反演求出 f ( n ) f(n) f(n)

前置知识

数论分块

点这里

狄利克雷卷积

还不会,先鸽着

积性函数

T T T 为函数 f f f 的定义域。如果 ∀ a , b ∈ T 且 g c d ( a , b ) = 1 \forall a,b \in T 且 gcd(a,b)=1 a,bTgcd(a,b)=1 都有 f ( a b ) = f ( a ) × f ( b ) f(ab) = f(a) \times f(b) f(ab)=f(a)×f(b),那么称函数 f f f 为积性函数。

一个很好的性质是几乎所有积性函数都能用 线性筛 筛出来。

常见的积性函数有欧拉函数和莫比乌斯函数

莫比乌斯函数

定义

μ ( n ) \mu(n) μ(n) 为莫比乌斯函数,定义为:

μ ( n ) = { 1   n = 1 0   n 含有平方因子 ( − 1 ) k   k 为 n 本质不同的质因子数 \mu(n) = \begin{cases} 1 & \text{ } n=1 \\ 0 & \text{ } n含有平方因子 \\ (-1)^k & \text{ } k为n本质不同的质因子数 \end{cases} μ(n)= 10(1)k n=1 n含有平方因子 kn本质不同的质因子数

性质

  1. 莫比乌斯函数是积性函数
  2. ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}\mu(d) = [n=1] dnμ(d)=[n=1]

第二条性质非常重要,可以将一个逻辑表达式的值转化为对 μ \mu μ 函数求和。下面证明一下:

  1. n = 1 n = 1 n=1 时: ∑ d ∣ n μ ( d ) = μ ( 1 ) = 1 \sum_{d|n}\mu(d) = \mu(1) = 1 dnμ(d)=μ(1)=1,显然等于右边。

  2. n ≠ 1 n \ne 1 n=1 时:我们考虑写出 n n n 质因数分解后的形式 n = p 1 c 1 × p 2 c 2 × . . . × p k c k n = p_1^{c_1} \times p_2^{c_2} \times ... \times p_k^{c_k} n=p1c1×p2c2×...×pkck。然后如果一个因子 d d d 含有某个 p 1 ∼ p k p_1 \sim p_k p1pk 的幂次大于 1 1 1,那么根据莫比乌斯函数的定义,此时 μ ( d ) = 0 \mu(d) = 0 μ(d)=0。因此我们只需要考虑每个质因数是否出现在 d d d 即可。

    那么有 ∑ d ∣ n μ ( d ) = ∑ i = 0 k ( k i ) ( − 1 ) i = ( 1 − 1 ) k = 0 \sum_{d|n}\mu(d) = \sum_{i = 0}^{k} \binom{k}{i}(-1)^i = (1 -1)^k = 0 dnμ(d)=i=0k(ik)(1)i=(11)k=0

综上,得证。

补充结论: [ g c d ( i , j ) = 1 ] = ∑ d ∣ g c d ( i , j ) μ ( d ) [gcd(i, j) = 1] = \sum_{d|gcd(i, j)}\mu(d) [gcd(i,j)=1]=dgcd(i,j)μ(d)

这个由上述性质可以直接得到。

求莫比乌斯函数模版:

int mu[N], prime[N], tot;
bool vis[N];
void pre() {
	mu[1] = 1; // 注意1是特殊的
	for(int i = 2; i < N; i ++ ) {
		if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;} // 根据定义,质数的莫比乌斯函数为-1 
		for(int j = 1; i * prime[j] < N && j <= tot; j ++ ) {
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) {
				mu[i * prime[j]] = 0; // 含有平方因子,莫比乌斯函数为0 
				break;
			}
			mu[i * prime[j]] = -mu[i]; // 根据积性函数的性质, mu[i * prime[j]] = mu[i] * mu[prime[j]] = -mu[i] 
		}
	} 
}

莫比乌斯反演

因子形式

F ( n ) = ∑ d ∣ n f ( d ) F(n) = \sum_{d|n}f(d) F(n)=dnf(d),则 f ( n ) = ∑ d ∣ n μ ( n d ) F ( d ) f(n) = \sum_{d|n}\mu(\frac{n}{d})F(d) f(n)=dnμ(dn)F(d)

证明:
F ( d ) F(d) F(d) 带入,那么有:
∑ d ∣ n μ ( n d ) F ( d ) \sum_{d|n}\mu(\frac{n}{d})F(d) dnμ(dn)F(d)
= ∑ d ∣ n μ ( n d ) ∑ k ∣ d f ( k ) = \sum_{d|n}\mu(\frac{n}{d})\sum_{k|d}f(k) =dnμ(dn)kdf(k)
= ∑ k ∣ n f ( k ) ∑ t ∣ n k μ ( t ) = \sum_{k|n}f(k)\sum_{t|\frac{n}{k}}\mu(t) =knf(k)tknμ(t)

根据莫比乌斯函数的性质 2 2 2:当 n k = 1 \frac{n}{k} = 1 kn=1 时,右边的和式为 1 1 1,此时 n = k n = k n=k,那么贡献为 f ( n ) 。 f(n)。 f(n) 否则右边的和式为 0 0 0 k k k 没有贡献。

因此结果为 右边 = f ( n ) = 左边 右边=f(n)=左边 右边=f(n)=左边

倍数形式

F ( n ) = ∑ n ∣ d f ( d ) F(n) = \sum_{n|d}f(d) F(n)=ndf(d),则 f ( n ) = ∑ n ∣ d μ ( d n ) F ( d ) f(n) = \sum_{n|d}\mu(\frac{d}{n})F(d) f(n)=ndμ(nd)F(d)

证明:
F ( d ) F(d) F(d) 带入,那么有:
∑ n ∣ d μ ( d n ) F ( d ) \sum_{n|d}\mu(\frac{d}{n})F(d) ndμ(nd)F(d)
= ∑ n ∣ d μ ( d n ) ∑ d ∣ k f ( k ) =\sum_{n|d}\mu(\frac{d}{n})\sum_{d|k}f(k) =ndμ(nd)dkf(k)
= ∑ n ∣ k f ( k ) ∑ t ∣ k n μ ( t ) =\sum_{n|k}f(k)\sum_{t|\frac{k}{n}}\mu(t) =nkf(k)tnkμ(t)

只有当 k n = 1 \frac{k}{n} =1 nk=1 时, f ( k ) f(k) f(k) 有贡献。此时 k = n k = n k=n,只有一个 f ( n ) f(n) f(n) 算入到了答案中。

因此 右边 = f ( n ) = 左边 右边=f(n)=左边 右边=f(n)=左边

实际上,上述两个形式并不太常用。通常情况下只用到 [ g c d ( i , j ) = 1 ] = ∑ d ∣ g c d ( i , j ) μ ( d ) [gcd(i, j) = 1] = \sum_{d|gcd(i, j)}\mu(d) [gcd(i,j)=1]=dgcd(i,j)μ(d) 这一个性质就足够了。但是回归本质:如果我们发现 F F F 函数好求,而 f f f 函数不好求,并且 F F F f f f 函数满足上述两种关系之一。那么我们可以通过求出 F F F 来反演得到 f f f

例题

周期性字符串

点这里
题意:
如果一个字符串 s s s 可以由另一个字符串 t ( t ≠ s ) t(t\ne s) t(t=s) 重复若干次得到,那么称 s s s 具有周期性。给定一个 n n n,求有多少长度为 n n n 的周期字符串。 1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1n106

分析:

首先可以 d p dp dp。设 f i f_{i} fi 表示长度为 i i i 的周期性字符串个数。那么有转移 f i = ∑ j ∣ i 2 6 j − f j f_{i} = \sum\limits_{j|i}26^j - f_j fi=ji26jfj。预处理 26 26 26 的幂次复杂度可做到 O ( n ln ⁡ n ) O(n\ln n) O(nlnn)。能够通过本题,但是如果 n n n 开到 1 0 7 10^7 107 就容易寄。

我们考虑莫比乌斯反演。设 g i g_i gi 表示长度为 i i i 且非周期性字符串个数,设 h i h_i hi 表示所有长度为 i i i 的因数的非周期性字符串个数。那么显然 h n = ∑ d ∣ n g ( d ) h_n = \sum_{d|n}g(d) hn=dng(d),答案就是 ∑ d ∣ n , d ≠ n g ( d ) \sum_{d|n,d \ne n}g(d) dn,d=ng(d)

我们发现 h i = 2 6 i h_i = 26^i hi=26i。这是因为任意一个长度为 i i i 的字符串 s s s 都可以对应一个最小的长度是 i i i 因数的非周期字符串 t t t。任意一个长度为 i i i 因数非周期性字符串 t t t 也都可以通过复制拼接唯一对应一个长度为 i i i 的字符串 s s s。因此这建立了一个双射,所以二者是相等的。

然后我们只需要把所以 d ∣ n , d ≠ n d|n,d\ne n dn,d=n g ( d ) g(d) g(d) h h h 反演出来即可。时间复杂度 O ( n × n = n ) O(\sqrt{n} \times \sqrt{n} = n) O(n ×n =n)

CODE:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
typedef long long LL;
const LL mod = 1e9 + 7;
int n, mu[N], prime[N], tot;
bool vis[N];
LL f[N], F[N], res;
inline LL Pow(LL x, LL y) {
	LL res = 1LL, k = x;
	while(y) {
		if(y & 1) res = (res * k) % mod;
		y >>= 1;
		k = (k * k) % mod;
	}
	return res;
}
void Get_mu() {
	mu[1] = 1;
	for(int i = 2; i < N; i ++ ) {
		if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;}
		for(int j = 1; j <= tot && i * prime[j] < N; j ++ ) {
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				break;
			}
			mu[i * prime[j]] = -mu[i];
  		}
	}
}
void calc(int n) {
	for(int d = 1; d * d <= n; d ++ ) {
		if(n % d == 0) {
			F[n] = ((F[n] + 1LL * mu[d] * f[n / d] % mod) % mod + mod) % mod;
			if(d * d != n) F[n] = ((F[n] + 1LL * mu[n / d] * f[d] % mod) % mod + mod) % mod;
		}
	}
}
int main() {
	Get_mu();
	cin >> n;
	for(int i = 1; i * i <= n; i ++ ) {
		if(n % i == 0) {
			f[i] = Pow(26LL, 1LL * i);
			f[n / i] = Pow(26LL, 1LL * (n / i));
		}
	}
	for(int i = 1; i * i <= n; i ++ ) {
		if(n % i == 0 && i != n) {
			calc(i); res = (res + F[i]) % mod;
			if(i * i != n && n / i != n) calc(n / i), res = (res + F[n / i]) % mod;
		}
	}
	cout << res << endl;
	return 0;
}

互质数对

点这里
题意:
给定一个 n n n,求 ∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = 1 ] \sum_{i = 1}^{n}\sum_{j = 1}^{n}[gcd(i, j) = 1] i=1nj=1n[gcd(i,j)=1] n ≤ 1 0 7 n \leq 10^7 n107

分析:
看到 [ g c d ( i , j ) = 1 ] [gcd(i, j) = 1] [gcd(i,j)=1],想到莫比乌斯函数
∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = 1 ] \sum_{i = 1}^{n}\sum_{j = 1}^{n}[gcd(i, j) = 1] i=1nj=1n[gcd(i,j)=1]
= ∑ i = 1 n ∑ j = 1 n ∑ d ∣ g c d ( i , j ) μ ( d ) =\sum_{i = 1}^{n}\sum_{j = 1}^{n}\sum\limits_{d|gcd(i, j)}\mu(d) =i=1nj=1ndgcd(i,j)μ(d)

把枚举质因数提前

= ∑ d = 1 n μ ( d ) × ⌊ n d ⌋ × ⌊ n d ⌋ =\sum_{d=1}^{n}\mu(d) \times \left \lfloor \frac{n}{d} \right \rfloor \times \left \lfloor \frac{n}{d} \right \rfloor =d=1nμ(d)×dn×dn

处理出 u ( i ) u(i) u(i) 的前缀和,然后一维数论分块就行。
复杂度 O ( n ) O(\sqrt{n}) O(n )

CODE:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7 + 10;
int n, prime[N], tot;
bool vis[N];
LL mu[N], sum[N], res;
void Get_mu() {
	mu[1] = 1;
	for(int i = 2; i < N; i ++ ) {
		if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;}
		for(int j = 1; j <= tot && 1LL * i * prime[j] < 1LL * N; j ++ ) {
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				break;
			}
			mu[i * prime[j]] = -mu[i];
		}
	}
	for(int i = 1; i < N; i ++ ) sum[i] = sum[i - 1] + mu[i];
}
int main() {
	Get_mu();
	cin >> n;
	int l = 1, r;
	while(l <= n) {
		r = n / (n / l);
		res = res + (sum[r] - sum[l - 1]) * (1LL * n / l) * (1LL * n / l);
		l = r + 1;
	}
	cout << res << endl;
	return 0;
}

[NOI2010D1T1]能量采集

点这里
题意:给你 n , m n, m n,m,求 ∑ i = 1 n ∑ j = 1 m 2 × g c d ( i , j ) − 1 \sum_{i = 1}^{n}\sum_{j = 1}^{m}2 \times gcd(i, j) - 1 i=1nj=1m2×gcd(i,j)1 1 ≤ n , m ≤ 1 0 5 1 \leq n, m \leq10^5 1n,m105

分析:
看到 g c d ( i , j ) gcd(i, j) gcd(i,j),可以考虑莫比乌斯函数
枚举 g c d gcd gcd
∑ i = 1 n ∑ j = 1 m 2 × g c d ( i , j ) − 1 \sum_{i = 1}^{n}\sum_{j = 1}^{m}2 \times gcd(i, j) - 1 i=1nj=1m2×gcd(i,j)1
= ∑ i = 1 n ∑ j = 1 m ∑ d = 1 m i n ( n , m ) ( 2 × d − 1 ) × ( [ g c d ( i , j ) = d ] ) =\sum_{i = 1}^{n}\sum_{j =1}^{m}\sum_{d=1}^{min(n, m)}(2 \times d - 1) \times ([gcd(i, j) = d]) =i=1nj=1md=1min(n,m)(2×d1)×([gcd(i,j)=d])

提前枚举 g c d gcd gcd

= ∑ d = 1 m i n ( n , m ) ( 2 × d − 1 ) × ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] =\sum_{d = 1}^{min(n,m)}(2\times d- 1) \times \sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i, j) = d] =d=1min(n,m)(2×d1)×i=1nj=1m[gcd(i,j)=d]

右边的两个求和符号上指标同时除以 d d d,变为枚举 d d d 的倍数:

= ∑ d = 1 m i n ( n , m ) ( 2 × d − 1 ) × ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = 1 ] =\sum_{d = 1}^{min(n, m)}(2 \times d - 1) \times \sum_{i = 1}^{\left \lfloor \frac{n}{d} \right \rfloor} \sum_{j = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}[gcd(i, j) = 1] =d=1min(n,m)(2×d1)×i=1dnj=1dm[gcd(i,j)=1]

= ∑ d = 1 m i n ( n , m ) ( 2 × d − 1 ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ k ∣ g c d ( i , j ) μ ( k ) =\sum_{d = 1}^{min(n, m)}(2 \times d - 1)\sum_{i = 1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}\sum_{k|gcd(i, j)}\mu(k) =d=1min(n,m)(2×d1)i=1dnj=1dmkgcd(i,j)μ(k)
= ∑ d = 1 m i n ( n , m ) ( 2 × d − 1 ) ∑ k = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( k ) × ⌊ n d k ⌋ × ⌊ m d k ⌋ =\sum_{d = 1}^{min(n, m)}(2\times d - 1)\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor, \left \lfloor \frac{m}{d} \right \rfloor)}\mu(k) \times \left \lfloor \frac{n}{dk} \right \rfloor \times \left \lfloor \frac{m}{dk} \right \rfloor =d=1min(n,m)(2×d1)k=1min(dn,dm)μ(k)×dkn×dkm

然后考虑对于 d d d 而言, ⌊ n d ⌋ \left \lfloor \frac{n}{d} \right \rfloor dn ⌊ m d ⌋ \left \lfloor \frac{m}{d} \right \rfloor dm 成块状分布,可以二维分块得到 n ′ = ⌊ n d ⌋ n' = \left \lfloor \frac{n}{d} \right \rfloor n=dn m ′ = ⌊ m d ⌋ m' = \left \lfloor \frac{m}{d} \right \rfloor m=dm。对于 n ′ n' n m ′ m' m 分别相同的的一段内,右边和式的贡献是相同的,变成了 ∑ k = 1 m i n ( n ′ , m ′ ) μ ( k ) × ⌊ n ′ k ⌋ × ⌊ m ′ k ⌋ \sum_{k = 1}^{min(n', m')}\mu(k) \times \left \lfloor \frac{n'}{k} \right \rfloor \times \left \lfloor \frac{m'}{k} \right \rfloor k=1min(n,m)μ(k)×kn×km。也可以再二维整除分块一次,总复杂度就是 O ( n × n = n ) O(\sqrt{n} \times \sqrt{n} = n) O(n ×n =n) 的。

实际上复杂度还能做到 O ( n ) O(\sqrt{n}) O(n ),这个后边会讲。

CODE:

// 整除套整除分块 好像能做到 O(n) 
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
int n, m, prime[N], tot;
LL mu[N], sum[N], res;
bool vis[N];
void Get_mu() {
	mu[1] = 1;
	for(int i = 2; i < N; i ++ ) {
		if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;}
		for(int j = 1; j <= tot && i * prime[j] < N; j ++ ) {
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				break;
			}
			mu[i * prime[j]] = -mu[i]; // 积性函数性质 
		}
	}
	for(int i = 1; i < N; i ++ ) sum[i] = sum[i - 1] + mu[i];
} 
LL calc(int nt, int mt) {
	int t = min(nt, mt);
	int l = 1, r; LL res = 0;
	while(l <= t) {
		r = min({t, nt / (nt / l), mt / (mt / l)});
		res = res + 1LL * (nt / l) * (mt / l) * (sum[r] - sum[l - 1]);
		l = r + 1;
	}
	return res;
}
int main() {
	scanf("%d%d", &n, &m);
	Get_mu();
	int t = min(n, m);
	int l = 1, r;
	while(l <= t) {
		r = min({t, n / (n / l), m / (m / l)});
		res = res + calc(n / l, m / l) * (1LL * (l + r) * (r - l + 1) - 1LL * (r - l + 1));
		l = r + 1;
	}
	printf("%lld\n", res);
	return 0;
}

BZOJ2820 YY的GCD

点这里
题意:
T T T 次询问,每次给你 n , m n,m n,m,问 ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = p ] , p 代表质数 \sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i, j) = p],p代表质数 i=1nj=1m[gcd(i,j)=p],p代表质数
T ≤ 1 0 4 , 1 ≤ n , m ≤ 1 0 7 T \leq 10^4,1 \leq n,m \leq 10^7 T104,1n,m107

分析:
首先看到 逻辑表达式的值 和 g c d ( i , j ) gcd(i, j) gcd(i,j),考虑莫反。
如果我们定义 f ( x ) = { 1   x ∈ P 0   x ∉ P f(x) = \begin{cases} 1 & \text{ } x\in P \\ 0 & \text{ } x \notin P \end{cases} f(x)={10 xP x/P P P P 代表素数集合。那么所要求的就是:
∑ i = 1 n ∑ j = 1 m f ( g c d ( i , j ) ) \sum_{i=1}^{n}\sum_{j = 1}^{m}f(gcd(i, j)) i=1nj=1mf(gcd(i,j))
= ∑ d = 1 m i n ( n , m ) f ( d ) ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] =\sum_{d=1}^{min(n, m)}f(d) \sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i, j) = d] =d=1min(n,m)f(d)i=1nj=1m[gcd(i,j)=d]
= ∑ d = 1 m i n ( n , m ) f ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = 1 ] =\sum_{d = 1}^{min(n, m)}f(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] =d=1min(n,m)f(d)i=1dnj=1dm[gcd(i,j)=1]
= ∑ d = 1 m i n ( n , m ) f ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ∑ k ∣ g c d ( i , j ) μ ( k ) =\sum_{d = 1}^{min(n, m)}f(d)\sum_{i = 1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}\sum_{k|gcd(i, j)}\mu(k) =d=1min(n,m)f(d)i=1dnj=1dmkgcd(i,j)μ(k)
= ∑ d = 1 m i n ( n , m ) f ( d ) ∑ k = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( k ) × ⌊ n d k ⌋ × ⌊ m d k ⌋ =\sum_{d = 1}^{min(n, m)}f(d)\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor,\left \lfloor \frac{m}{d} \right \rfloor)}\mu(k) \times \left \lfloor \frac{n}{dk} \right \rfloor \times \left \lfloor \frac{m}{dk} \right \rfloor =d=1min(n,m)f(d)k=1min(dn,dm)μ(k)×dkn×dkm

首先 f f f 数组的求解是简单的,可以在 O ( N ) O(N) O(N) 的复杂度内预处理出来。然后我们可以使用上一题的思路,二次分块,但是那样每次询问复杂度是 O ( n ) O(n) O(n) 的,总复杂度就变成 O ( T n ) O(Tn) O(Tn),无法接受。

怎么办呢?我们好像已经化到了最简,无法进一步优化了。

这里有一个很通用的技巧,也算一个很重要的套路:

当我们需要二次分块时,一般第二个求和符号后面的分母都是两个变量的乘积。尝试把乘积提前枚举,并将式子变形。往往能得到一个可以预处理的函数,使得复杂度少一个 n \sqrt{n} n

我们来尝试一下:

首先理解一下式子的含义:相当于把所有满足 d k ≤ m i n ( n , m ) dk \leq min(n, m) dkmin(n,m) 的数对 ( d , k ) (d, k) (d,k) 都贡献一个 f ( d ) × μ ( k ) × ⌊ n d k ⌋ ⌊ m d k ⌋ f(d) \times \mu(k) \times \left \lfloor \frac{n}{dk} \right \rfloor \left \lfloor \frac{m}{dk} \right \rfloor f(d)×μ(k)×dkndkm 到答案中。

d k = T dk = T dk=T,那么有:
∑ d = 1 m i n ( n , m ) f ( d ) ∑ k = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( k ) × ⌊ n d k ⌋ × ⌊ m d k ⌋ \sum_{d = 1}^{min(n, m)}f(d)\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor,\left \lfloor \frac{m}{d} \right \rfloor)}\mu(k) \times \left \lfloor \frac{n}{dk} \right \rfloor \times \left \lfloor \frac{m}{dk} \right \rfloor d=1min(n,m)f(d)k=1min(dn,dm)μ(k)×dkn×dkm
= ∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ × ⌊ m T ⌋ ∑ d ∣ T f ( d ) × μ ( T d ) =\sum_{T = 1}^{min(n, m)} \left \lfloor \frac{n}{T} \right \rfloor \times \left \lfloor \frac{m}{T} \right \rfloor \sum_{d|T}f(d) \times \mu(\frac{T}{d}) =T=1min(n,m)Tn×TmdTf(d)×μ(dT)

F ( T ) = ∑ d ∣ T f ( d ) × μ ( T d ) F(T) = \sum_{d|T}f(d) \times \mu(\frac{T}{d}) F(T)=dTf(d)×μ(dT)。那么原式变成了

∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ × ⌊ m T ⌋ × F ( T ) \sum_{T = 1}^{min(n, m)}\left \lfloor \frac{n}{T} \right \rfloor \times \left \lfloor \frac{m}{T} \right \rfloor \times F(T) T=1min(n,m)Tn×Tm×F(T)

我们发现 F ( T ) F(T) F(T) 是可以在 O ( N ln ⁡ N ) O(N\ln N) O(NlnN) 的复杂度内预处理出来的,因此每次询问只需要一次二维数论块就可以了。

为什么这样做复杂度就能够优化呢?
我们考虑变形前的式子中 两个求和符号都是与每次给定的 n n n m m m 相关的,这样就无法预处理。而如果我们让第一个求和符号枚举乘积,第二个求和符号枚举因子,那么第二个求和就只与乘积 T T T的大小有关,与 n , m n, m n,m 无关了。因此一般可以预处理。

实际上本题的预处理可以做到线性。一些较难的题目也会在预处理上做文章,需要线性的做法才能通过。而 F F F 数组大多都是两个函数的 卷积,可以根据卷积的一些性质来求。

总复杂度就是 O ( N ln ⁡ N + T n ) O(N \ln N + T\sqrt{n}) O(NlnN+Tn ),其中 N N N n n n 的上限。

CODE:

// 好像会线性了? 
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e7 + 10;
LL f[N], sum[N];
int T, n, m, mu[N];
int prime[N], tot;
bool vis[N];
void Get_mu() {
	mu[1] = 1;
	for(int i = 2; i < N; i ++ ) {
		if(!vis[i]) {prime[++ tot] = i; mu[i] = -1; f[i] = 1LL;}
		for(int j = 1; j <= tot && 1LL * i * prime[j] < 1LL * N; j ++ ) {
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				f[i * prime[j]] = mu[i];
				break;
			}
			mu[i * prime[j]] = -mu[i];
			f[i * prime[j]] = -f[i] + mu[i];
		}
	}
	for(int i = 1; i < N; i ++ ) sum[i] = sum[i - 1] + f[i];
}
LL calc(int n, int m) {
	int l = 1, r; LL res = 0;
	int t = min(n, m);
	while(l <= t) {
		r = min({t, n / (n / l), m / (m / l)});
		res = res + 1LL * (n / l) * (m / l) * (sum[r] - sum[l - 1]);
		l = r + 1;
	}
	return res;
}
int main() {
	Get_mu();
	scanf("%d", &T);
	while(T -- ) {
		scanf("%d%d", &n, &m);
		printf("%lld\n", calc(n, m));
	}
	return 0;
}

[SDOI2015] 约数个数和

点这里
题意:设 d ( x ) d(x) d(x) x x x 的因数个数。 T T T 次询问,每次给定 n , m n, m n,m,求 ∑ i = 1 n ∑ j = 1 m d ( i × j ) \sum_{i = 1}^{n}\sum_{j = 1}^{m}d(i \times j) i=1nj=1md(i×j)
T ≤ 5 × 1 0 4 , 1 ≤ n , m ≤ 5 × 1 0 4 T \leq 5 \times 10^4,1\leq n, m \leq 5 \times 10^4 T5×1041n,m5×104

首先有一个对本题而言非常重要的结论:
d ( x × y ) = ∑ i ∣ x ∑ j ∣ y [ g c d ( i , j ) = 1 ] d(x \times y) = \sum_{i|x}\sum_{j|y}[gcd(i, j) = 1] d(x×y)=ixjy[gcd(i,j)=1]

证明一下:
P ( n ) P(n) P(n) 表示 n n n 质因数分解后的质因子集合。
那么设 P ( x × y ) = { p 1 , p 2 , . . . , p k } P(x \times y) = \left \{ p_1,p_2,...,p_k\right \} P(x×y)={p1,p2,...,pk}

x = p 1 c 1 p 2 c 2 . . . p k c k x = p_1^{c_1} p_2^{c_2}...p_k^{c_k} x=p1c1p2c2...pkck
y = p 1 t 1 p 2 t 2 . . . p k t k y = p_1^{t_1}p_2^{t_2}...p_k^{t_k} y=p1t1p2t2...pktk

那么很显然 x × y x \times y x×y 的因子数量为 ∏ i = 1 k ( c i + t i + 1 ) \prod_{i=1}^{k}(c_i+t_i+1) i=1k(ci+ti+1)
然后考虑 ∑ i ∣ x ∑ j ∣ y [ g c d ( i , j ) = 1 ] \sum_{i|x}\sum_{j|y}[gcd(i, j) = 1] ixjy[gcd(i,j)=1] 什么时候会对答案有贡献。
需要对于 p 1 ∼ p k p_1 \sim p_k p1pk k k k 个质因数, x x x 不含 或者 y y y 不含。

对于一个素数 p i p_i pi

  1. x x x 不含, y y y 含。这种情况有 t i t_i ti
  2. x x x 含, y y y 不含。这种情况有 c i c_i ci
  3. x x x 不含,且 y y y 不含。这种情况有 1 1 1 种。

因此每一个 i i i 的方案数都是都是 c i + t i + 1 c_i + t_i + 1 ci+ti+1 种。由于是分步。需要把它们乘起来,总方案数就是 ∏ i = 1 k ( c i + t i + 1 ) \prod_{i=1}^{k}(c_i + t_i + 1) i=1k(ci+ti+1)。每一种方案都唯一对应了一个数对 ( p , q ) (p, q) (p,q),满足 p ∣ x , q ∣ y p|x,q|y px,qy g c d ( p , q ) = 1 gcd(p, q) = 1 gcd(p,q)=1

那么原问题就变成求 ∑ i = 1 n ∑ j = 1 m ∑ p ∣ i ∑ q ∣ j [ g c d ( p , q ) = 1 ] \sum_{i = 1}^{n}\sum_{j=1}^{m}\sum_{p|i}\sum_{q|j}[gcd(p,q )=1] i=1nj=1mpiqj[gcd(p,q)=1]
= ∑ i = 1 n ∑ j = 1 m ∑ p ∣ i ∑ q ∣ j ∑ k ∣ g c d ( p , q ) μ ( k ) =\sum_{i = 1}^{n}\sum_{j=1}^{m}\sum_{p|i}\sum_{q|j}\sum_{k|gcd(p, q)}\mu(k) =i=1nj=1mpiqjkgcd(p,q)μ(k)

变成先枚举 k k k,再枚举 p , q p,q p,q,最后枚举 i , j i, j i,j 的形式

= ∑ k = 1 m i n ( n , m ) μ ( k ) ∑ p = 1 ⌊ n k ⌋ ∑ q = 1 ⌊ m k ⌋ ⌊ n k p ⌋ × ⌊ m k q ⌋ =\sum_{k = 1}^{min(n, m)}\mu(k)\sum_{p = 1}^{\left \lfloor \frac{n}{k} \right \rfloor}\sum_{q=1}^{\left \lfloor \frac{m}{k} \right \rfloor} \left \lfloor \frac{n}{kp} \right \rfloor \times \left \lfloor \frac{m}{kq} \right \rfloor =k=1min(n,m)μ(k)p=1knq=1kmkpn×kqm

= ∑ k = 1 m i n ( n , m ) μ ( k ) × ∑ p = 1 ⌊ n k ⌋ ⌊ n k p ⌋ × ∑ q = 1 ⌊ m k ⌋ ⌊ m k q ⌋ =\sum_{k=1}^{min(n, m)}\mu(k) \times \sum_{p=1}^{\left \lfloor \frac{n}{k} \right \rfloor} \left \lfloor \frac{n}{kp} \right \rfloor \times \sum_{q=1}^{\left \lfloor \frac{m}{k} \right \rfloor}\left \lfloor \frac{m}{kq} \right \rfloor =k=1min(n,m)μ(k)×p=1knkpn×q=1kmkqm

f ( n ) = ∑ i = 1 n ⌊ n i ⌋ f(n) = \sum_{i=1}^{n}\left \lfloor \frac{n}{i} \right \rfloor f(n)=i=1nin

那么原式就是

= ∑ k = 1 m i n ( n , m ) μ ( k ) × f ( ⌊ n k ⌋ ) × f ( ⌊ m k ⌋ ) =\sum_{k=1}^{min(n, m)}\mu(k)\times f(\left \lfloor \frac{n}{k} \right \rfloor) \times f(\left \lfloor \frac{m}{k} \right \rfloor) =k=1min(n,m)μ(k)×f(kn)×f(km)

然后数论分块预处理 f f f,每次询问一维数论分块求出答案即可。
时间复杂度 O ( n n + T n ) O(n\sqrt{n} + T \sqrt{n}) O(nn +Tn )

CODE:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e4 + 10;
int T, n, m;
int mu[N], prime[N], tot, sum[N];
LL S[N];
bool vis[N];
void get_mu() {
	mu[1] = 1;
	for(int i = 2; i < N; i ++ ) {
		if(!vis[i]) {prime[++ tot] = i, mu[i] = -1;}
		for(int j = 1; j <= tot && i * prime[j] < N; j ++ ) {
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				break;
			}
			mu[i * prime[j]] = -mu[i];
		}
	}
	for(int i = 1; i < N; i ++ ) sum[i] = sum[i - 1] + mu[i];
}
void getS(int n) {
	int l = 1, r; 
	while(l <= n) {
		r = n / (n / l);
		S[n] = (S[n] + 1LL * (r - l + 1) * (n / l));
		l = r + 1;
	}
}
inline LL calc(int n, int m) {
	int t = min(n, m);
	int l = 1, r; LL res = 0;
	while(l <= t) {
		r = min({t, n / (n / l), m / (m / l)});
		res = res + S[n / l] * S[m / l] * 1LL * (sum[r] - sum[l - 1]);
		l = r + 1;
	}
	return res;
}
int main() {
	get_mu();
	for(int i = 1; i < N; i ++ ) getS(i);
	scanf("%d", &T);
	while(T -- ) {
		scanf("%d%d", &n, &m);
		printf("%lld\n", calc(n, m));
	}
	return 0;
}

BZOJ4407 于神之怒加强版

点这里
题意:
给你 T , k T,k T,k T T T次询问,每次询问给定 n , m n,m n,m,求 ∑ i = 1 n ∑ j = 1 m g c d ( i , j ) k   m o d   1 0 9 + 7 \sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i, j)^k \ mod \ 10^9+7 i=1nj=1mgcd(i,j)k mod 109+7
1 ≤ T ≤ 2 × 1 0 3 1 \leq T \leq 2 \times 10^3 1T2×103 1 ≤ n , m ≤ 5 × 1 0 6 1 \leq n, m \leq 5 \times 10^6 1n,m5×106

分析:
看到 g c d gcd gcd,还是想到枚举 g c d gcd gcd,然后乘一个逻辑表达式。

∑ i = 1 n ∑ j = 1 m g c d ( i , j ) k   m o d   1 0 9 + 7 \sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i, j)^k \ mod \ 10^9+7 i=1nj=1mgcd(i,j)k mod 109+7
= ∑ d = 1 m i n ( n , m ) d k ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] =\sum_{d=1}^{min(n,m)}d^k\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i, j) = d] =d=1min(n,m)dki=1nj=1m[gcd(i,j)=d]

右边的两个求和老演员了。

= ∑ d = 1 m i n ( n , m ) d k ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = 1 ] =\sum_{d=1}^{min(n, m)}d^k \sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor} \sum_{j=1}^{\left \lfloor \frac{m}{d} \right \rfloor}[gcd(i, j)=1] =d=1min(n,m)dki=1dnj=1dm[gcd(i,j)=1]
= ∑ d = 1 m i n ( n , m ) d k ∑ t = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( t ) × ⌊ n d t ⌋ × ⌊ m d t ⌋ =\sum_{d=1}^{min(n, m)}d^k \sum_{t = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor, \left \lfloor \frac{m}{d} \right \rfloor)}\mu(t) \times \left \lfloor \frac{n}{dt} \right \rfloor \times \left \lfloor \frac{m}{dt} \right \rfloor =d=1min(n,m)dkt=1min(dn,dm)μ(t)×dtn×dtm

= ∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ × ⌊ m T ⌋ ∑ d ∣ T d k × μ ( T d ) =\sum_{T=1}^{min(n, m)} \left \lfloor \frac{n}{T} \right \rfloor \times \left \lfloor \frac{m}{T} \right \rfloor \sum_{d|T} d^k \times \mu(\frac{T}{d}) =T=1min(n,m)Tn×TmdTdk×μ(dT)

F ( T ) = ∑ d ∣ T d k × μ ( T d ) F(T) = \sum_{d|T}d^k \times \mu(\frac{T}{d}) F(T)=dTdk×μ(dT)

= ∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ × ⌊ m T ⌋ × F ( T ) =\sum_{T=1}^{min(n, m)} \left \lfloor \frac{n}{T} \right \rfloor \times \left \lfloor \frac{m}{T} \right \rfloor \times F(T) =T=1min(n,m)Tn×Tm×F(T)

求出 F ( T ) F(T) F(T) 的前缀和每次询问一维数论分块就可以了。
时间复杂度 O ( n log ⁡ n + T n ) O(n\log n + T\sqrt{n}) O(nlogn+Tn )

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 5e6 + 10;
const LL mod = 1e9 + 7;
int T, n, m, tot;
bool vis[N];
LL k, mu[N], prime[N], f[N], val[N];
LL sum[N];
inline LL Pow(LL x, LL y) {
	LL res = 1LL, k = x;
	while(y) {
		if(y & 1) res = (res * k) % mod;
		y >>= 1;
		k = (k * k) % mod;
	}
	return res;
}
void pre() {
	for(int i = 1; i < N; i ++ ) val[i] = Pow(1LL * i, k);
	mu[1] = 1;
	for(int i = 2; i < N; i ++ ) {
		if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;}
        for(int j = 1; i * prime[j] < N && j <= tot; j ++ ) {
        	vis[i * prime[j]] = 1;
        	if(i % prime[j] == 0) {
        		mu[i * prime[j]] = 0;
        		break;
			}
			mu[i * prime[j]] = -mu[i];
		}
	}
	for(int i = 1; i < N; i ++ ) {
		for(int j = 1; j * i < N; j ++ ) {
			f[i * j] = (f[i * j] + mu[i] * val[j] % mod) % mod;
		}
	}
	for(int i = 1; i < N; i ++ ) sum[i] = (sum[i - 1] + f[i]) % mod;
}
LL calc(int n, int m) {
	LL res = 0;
	int l = 1, r, t = min(n, m);
	while(l <= t) {
		r = min({n / (n / l), m / (m / l), t});
		res = (res + ((sum[r] - sum[l - 1]) % mod + mod) % mod * 1LL * (n / l) % mod * (m / l) % mod) % mod;
		l = r + 1;
	}
	return res;
}
int main() {
	scanf("%d%lld", &T, &k);
	pre();
	while(T -- ) {
		scanf("%d%d", &n, &m);
		printf("%lld\n", calc(n, m));
	}
	return 0;
}

【BZOJ2693】jzptab最小公倍数之和

题意:
给你一个 T T T T T T组询问,每次给定一个 n , m n,m n,m。求 ∑ i = 1 n ∑ j = 1 m l c m ( i , j ) \sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j) i=1nj=1mlcm(i,j)。答案对 1 0 8 + 9 10^8 +9 108+9 取模。
1 ≤ T ≤ 1 0 4 1\leq T \leq 10^4 1T104 1 ≤ n , m ≤ 1 0 7 1 \leq n,m \leq 10^7 1n,m107

分析:
我们都知道 l c m ( i , j ) = i × j g c d ( i , j ) lcm(i, j) = \frac{i \times j}{gcd(i, j)} lcm(i,j)=gcd(i,j)i×j
所以有
∑ i = 1 n ∑ j = 1 m l c m ( i , j ) \sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j) i=1nj=1mlcm(i,j)
= ∑ i = 1 n ∑ j = 1 m i × j g c d ( i , j ) =\sum_{i=1}^{n}\sum_{j=1}^{m}\frac{i\times j}{gcd(i, j)} =i=1nj=1mgcd(i,j)i×j
= ∑ d = 1 m i n ( n , m ) 1 d ∑ i = 1 n ∑ j = 1 m i × j × [ g c d ( i , j ) = d ] =\sum_{d=1}^{min(n, m)}\frac{1}{d} \sum_{i = 1}^{n}\sum_{j=1}^{m}i\times j \times [gcd(i, j) = d] =d=1min(n,m)d1i=1nj=1mi×j×[gcd(i,j)=d]
= ∑ d = 1 m i n ( n , m ) 1 d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ ( i d ) × ( j d ) × [ g c d ( i , j ) = 1 ] =\sum_{d=1}^{min(n, m)}\frac{1}{d}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{m}{d} \right \rfloor}(id) \times (jd) \times [gcd(i, j) = 1] =d=1min(n,m)d1i=1dnj=1dm(id)×(jd)×[gcd(i,j)=1]
= ∑ d = 1 m i n ( n , m ) d ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ i × j × ∑ k ∣ g c d ( i , j ) μ ( k ) =\sum_{d=1}^{min(n, m)}d\sum_{i = 1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}i \times j \times \sum_{k|gcd(i, j)}\mu(k) =d=1min(n,m)di=1dnj=1dmi×j×kgcd(i,j)μ(k)
= ∑ d = 1 m i n ( n , m ) d ∑ k = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( k ) ∑ i = 1 ⌊ n d k ⌋ ∑ j = 1 ⌊ m d k ⌋ ( i k ) × ( j k ) =\sum_{d=1}^{min(n, m)}d\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor, \left \lfloor \frac{m}{d} \right \rfloor)}\mu(k)\sum_{i = 1}^{\left \lfloor \frac{n}{dk} \right \rfloor}\sum_{j = 1}^{\left \lfloor \frac{m}{dk} \right \rfloor}(ik) \times (jk) =d=1min(n,m)dk=1min(dn,dm)μ(k)i=1dknj=1dkm(ik)×(jk)
= ∑ d = 1 m i n ( n , m ) d ∑ k = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( k ) × k 2 × f ( ⌊ n d k ⌋ ) × f ( ⌊ m d k ⌋ ) =\sum_{d =1}^{min(n, m)}d\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor, \left \lfloor \frac{m}{d} \right \rfloor)}\mu(k) \times k^2 \times f(\left \lfloor \frac{n}{dk} \right \rfloor) \times f(\left \lfloor \frac{m}{dk} \right \rfloor) =d=1min(n,m)dk=1min(dn,dm)μ(k)×k2×f(dkn)×f(dkm)

其中 f ( n ) = ∑ i = 1 n i = n × ( n + 1 ) 2 f(n) = \sum_{i=1}^{n}i = \frac{n \times (n + 1)}{2} f(n)=i=1ni=2n×(n+1)

然后设 T = d k T = dk T=dk

= ∑ T = 1 m i n ( n , m ) f ( ⌊ n T ⌋ ) × f ( ⌊ m T ⌋ ) ∑ d ∣ T d 2 × ( T d ) × μ ( d ) =\sum_{T=1}^{min(n, m)}f(\left \lfloor \frac{n}{T} \right \rfloor) \times f(\left \lfloor \frac{m}{T} \right \rfloor) \sum_{d|T} d^2 \times (\frac{T}{d}) \times \mu(d) =T=1min(n,m)f(Tn)×f(Tm)dTd2×(dT)×μ(d)
= ∑ T = 1 m i n ( n , m ) f ( ⌊ n T ⌋ ) × f ( ⌊ m T ⌋ ) ∑ d ∣ T d × T × μ ( d ) =\sum_{T=1}^{min(n, m)}f(\left \lfloor \frac{n}{T} \right \rfloor) \times f(\left \lfloor \frac{m}{T} \right \rfloor) \sum_{d|T} d \times T \times \mu(d) =T=1min(n,m)f(Tn)×f(Tm)dTd×T×μ(d)

F ( T ) = T × ∑ d ∣ T d × μ ( d ) F(T) = T\times \sum_{d|T}d\times \mu(d) F(T)=T×dTd×μ(d)
F ( T ) F(T) F(T) 可以在 O ( n ln ⁡ n ) O(n \ln n) O(nlnn) 的复杂度内预处理,但这样还是会超时。

其实我们可以对于每个 T T T,求出 F ( T ) = ∑ d ∣ T d × μ ( d ) F(T) = \sum_{d|T}d\times \mu(d) F(T)=dTd×μ(d),然后再乘 T T T 就可以了。
接着发现 F F F 是积性函数,因为对于互质的 n , m n, m n,m F ( n m ) = F ( n ) × F ( m ) F(nm) = F(n) \times F(m) F(nm)=F(n)×F(m)。这是因为 n , m n, m n,m 互质,所以任意一个 n m nm nm 的因数 t t t 都能表示成 n n n 的一个因数 p p p m m m 的一个因数 q q q的形式。而由于莫比乌斯函数是积性函数,因此 t × μ ( t ) = p × q × μ ( p ) × μ ( q ) t\times\mu(t) = p \times q \times \mu(p)\times \mu(q) t×μ(t)=p×q×μ(p)×μ(q)。证明了 F F F 是积性函数。

我们考虑在线性筛的过程中求出 F F F 函数:
对于当前 i i i 和素数 p p p

  1. i % p ≠ 0 i \% p \ne 0 i%p=0,那么 F [ i × p ] = F [ i ] × F [ p ] F[i \times p] = F[i] \times F[p] F[i×p]=F[i]×F[p]。因为 i , p i,p i,p 互质
  2. i % p = 0 i \% p =0 i%p=0,那么 F [ i × p ] = F [ i ] F[i \times p] = F[i] F[i×p]=F[i]。分枚举因数 d d d 时包不包括新加进来的 p p p 两种情况考虑:如果不包括,那就是 F [ i ] F[i] F[i]。如果包括,那么此时 d d d p p p 的幂次一定大于 1 1 1 μ ( d ) = 0 \mu(d) = 0 μ(d)=0,没有贡献。
  3. 对于质数 p p p F [ p ] = 1 − p F[p] = 1 - p F[p]=1p

这样就能线性求出 F F F 了。然后每次询问一维数论分块就好了。
复杂度 O ( n + T n ) O(n + T\sqrt{n}) O(n+Tn )

CODE:

// 两次分块可以把复杂度做到O(n),但是如果多测就寄了
// 一般套路是考虑把第二次分块里的分母(不换元)提出来枚举,然后里面的函数观察发现是积性函数可以提前预处理 
// 然后就可以变成一次分块了。时间复杂度O(T√n) 
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod = 1e8 + 9;
const int N = 1e7 + 10;
int T, n, m, mu[N], prime[N], tot;
bool vis[N];
LL F[N];
void pre() {
	mu[1] = 1; F[1] = 1;
	for(int i = 2; i < N; i ++ ) {
		if(!vis[i]) {prime[++ tot] = i; mu[i] = -1; F[i] = (mod + 1LL - i);}
		for(int j = 1; 1LL * i * prime[j] < 1LL * N && j <= tot; j ++ ) {
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				F[i * prime[j]] = F[i];
				break;
			}
			mu[i * prime[j]] = -mu[i];
			F[i * prime[j]] = F[i] * F[prime[j]] % mod;
		}
	}
	for(int i = 1; i < N; i ++ ) {
		F[i] = F[i] * 1LL * i % mod;
		F[i] = (F[i] + F[i - 1]) % mod;
	}
}
inline LL f(int n) {
	return 1LL * (n + 1LL) * n / 2 % mod;
}
LL calc(int n, int m) {
	int t = min(n, m);
	int l = 1, r; LL res = 0;
	while(l <= t) {
		r = min({n / (n / l), m / (m / l), t});
		res = ((res + f(n / l) * f(m / l) % mod * (F[r] - F[l - 1]) % mod) % mod + mod) % mod;
		l = r + 1;
	}
	return res;
}
int main() {
	pre();
	scanf("%d", &T);
	while(T -- ) {
		scanf("%d%d", &n, &m);
		printf("%lld\n", calc(n, m));
	}
	return 0;
}

[bzoj3529-Sdoi2014] 数表

点这里
题意:
f ( n ) = ∑ d ∣ n d f(n) = \sum_{d|n}d f(n)=dnd Q Q Q 次询问,每次给你 n , m , a n, m, a n,m,a,求 ∑ i = 1 n ∑ j = 1 m f ( g c d ( i , j ) ) [ f ( g c d ( i , j ) ) ≤ a ] \sum_{i = 1}^{n}\sum_{j = 1}^{m}f(gcd(i, j))[f(gcd(i, j)) \leq a] i=1nj=1mf(gcd(i,j))[f(gcd(i,j))a]
1 ≤ n , m ≤ 1 0 5 1 \leq n, m \leq 10^5 1n,m105 1 ≤ Q ≤ 2 × 1 0 4 1 \leq Q \leq 2 \times 10^4 1Q2×104

分析:
[ f ( g c d ( i , j ) ) ≤ a ] [f(gcd(i, j)) \leq a] [f(gcd(i,j))a] 的限制不好做。
我们考虑设一个新函数 g ( n ) = { f ( n ) f ( n ) ≤ a 0 f ( n ) > a g(n) = \begin{cases} f(n) & \text{} f(n)\leq a \\ 0 & \text{} f(n) > a \end{cases} g(n)={f(n)0f(n)af(n)>a
那么式子变成了 ∑ i = 1 n ∑ j = 1 m g ( g c d ( i , j ) ) \sum_{i = 1}^{n}\sum_{j = 1}^{m}g(gcd(i, j)) i=1nj=1mg(gcd(i,j))
由于 g c d gcd gcd 的取值比较小,我们可以求出所有的 f ( n ) f(n) f(n)。然后按照 f ( n ) f(n) f(n) 从小到大的顺序把 n n n 排序,把询问按照 a a a 从小到大的顺序排序后,我们就可以每次加入一些 n n n,把他们的 g ( n ) g(n) g(n) 0 0 0 变为 f ( n ) f(n) f(n)

然后考虑上面的式子怎么求:
∑ i = 1 n ∑ j = 1 m g ( g c d ( i , j ) ) \sum_{i = 1}^{n}\sum_{j = 1}^{m}g(gcd(i, j)) i=1nj=1mg(gcd(i,j))
= ∑ d = 1 m i n ( n , m ) g ( d ) ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = d ] =\sum_{d=1}^{min(n, m)}g(d)\sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i, j) = d] =d=1min(n,m)g(d)i=1nj=1m[gcd(i,j)=d]
= ∑ d = 1 m i n ( n , m ) g ( d ) ∑ i = 1 ⌊ n d ⌋ ∑ j = 1 ⌊ m d ⌋ [ g c d ( i , j ) = 1 ] =\sum_{d=1}^{min(n, m)}g(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] =d=1min(n,m)g(d)i=1dnj=1dm[gcd(i,j)=1]
= ∑ d = 1 m i n ( n , m ) g ( d ) ∑ k = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( k ) × ⌊ n d k ⌋ × ⌊ m d k ⌋ =\sum_{d=1}^{min(n, m)}g(d)\sum_{k = 1}^{min(\left \lfloor \frac{n}{d} \right \rfloor,\left \lfloor \frac{m}{d} \right \rfloor)}\mu(k) \times \left \lfloor \frac{n}{dk} \right \rfloor \times \left \lfloor \frac{m}{dk} \right \rfloor =d=1min(n,m)g(d)k=1min(dn,dm)μ(k)×dkn×dkm
= ∑ T = 1 m i n ( n , m ) ⌊ n T ⌋ × ⌊ m T ⌋ ∑ d ∣ T g ( d ) × μ ( T d ) =\sum_{T = 1}^{min(n, m)} \left \lfloor \frac{n}{T} \right \rfloor \times \left \lfloor \frac{m}{T} \right \rfloor \sum_{d|T}g(d)\times \mu(\frac{T}{d}) =T=1min(n,m)Tn×TmdTg(d)×μ(dT)
F ( T ) = ∑ d ∣ T g ( d ) × μ ( T d ) F(T) = \sum_{d|T}g(d) \times \mu(\frac{T}{d}) F(T)=dTg(d)×μ(dT)

那么我们需要维护出来 F F F 的前缀和。
每次新加入一个 d d d 就把它的所有倍数 T T T 加上 f ( d ) × μ ( T d ) f(d) \times \mu(\frac{T}{d}) f(d)×μ(dT)。那么我们需要一个单点修改,区间求和的数据结构。树状数组就可以。时间复杂度是 调和级数 加上树状数组的复杂度。 O ( n log ⁡ 2 n + Q n log ⁡ n ) O(n\log^2 n + Q\sqrt{n}\log n) O(nlog2n+Qn logn)
CODE:

#include<bits/stdc++.h>
#define int long long
using namespace std; // 首先容易求出 f(d), 然后离线就好了 
const int N = 1e5 + 10;
const int mod = (1LL << 31);
int n, m, q;
int mu[N], prime[N], tot, idx[N];
bool vis[N];
int f[N], F[N], c[N], ans[N];
struct qq {
	int n, m, v, id;
}qc[N];
int lowbit(int x) {return x & -x;}
void add(int x, int y) {
	for(; x < N; x += lowbit(x)) c[x] = (c[x] + y) % mod;
}
int ask(int x) {
	int res = 0;
	for(; x; x -= lowbit(x)) res = (res + c[x]) % mod;
	return res;
}
void pre() {
	mu[1] = 1;
	for(int i = 2; i < N; i ++ ) {
		if(!vis[i]) {prime[++ tot] = i; mu[i] = -1;}
		for(int j = 1; i * prime[j] < N && j <= tot; j ++ ) {
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) {
				mu[i * prime[j]] = 0;
				break;
			}
	    	mu[i * prime[j]] = -mu[i];
		}
	}
	for(int i = 1; i < N; i ++ ) {
		for(int j = 1; j * i < N; j ++ ) {
			f[j * i] = (f[j * i] + (i)) % mod;
		}
	}
}
bool cmp(qq x, qq y) {
	return x.v < y.v;
}
bool cmpf(int x, int y) {
	return f[x] < f[y];
}
void Add(int x) { // 加入下标为x的 
	for(int i = 1; i * x < N; i ++ ) {
		add(i * x, (f[x] * mu[i] + mod) % mod);
	}
}
int calc(int n, int m) {
	int t = min(n, m);
	int l = 1, r; int res = 0;
	while(l <= t) {
		r = min({n / (n / l), m / (m / l), t});
		res = (res + (1LL * ((ask(r) - ask(l - 1)) % mod) * (n / l) % mod * (m / l) % mod) + mod) % mod;
		l = r + 1;
	}
	return res;
} 
signed main() {
	pre();
	scanf("%lld", &q);
	for(int i = 1; i <= q; i ++ ) {
		int v; scanf("%lld%lld%lld", &n, &m, &v);
		qc[i] = (qq) {n, m, v, i};
	}
	sort(qc + 1, qc + q + 1, cmp);
	for(int i = 1; i < N; i ++ ) idx[i] = i;
	sort(idx + 1, idx + N, cmpf);
	int now = 1;
	for(int i = 1; i <= q; i ++ ) {
		while(now < N && f[idx[now]] <= qc[i].v) {
			Add(idx[now]);
			now ++;
		}
		ans[qc[i].id] = calc(qc[i].n, qc[i].m);
	}
	for(int i = 1; i <= q; i ++ ) printf("%lld\n", ans[i]);
	return 0;
}

练习题

51nod 1675 序列变换

点这里
题意:
给你两个长度为 n n n 的序列 a , b a,b a,b,求有多少对 ( x , y ) (x,y) (x,y),满足以下两个条件:

  1. g c d ( x , y ) = 1 gcd(x, y) = 1 gcd(x,y)=1
  2. a b x = b a y a_{b_x} = b_{a_y} abx=bay

1 ≤ n ≤ 1 0 5 , 1 ≤ a i , b i ≤ n 1 \leq n \leq 10^5,1\leq a_i,b_i \leq n 1n105,1ai,bin

BZOJ3561 DZY Loves Math VI

点这里
题意:
给定正整数 n , m n,m n,m,求: ∑ i = 1 n ∑ j = 1 n l c m ( i , j ) g c d ( i , j ) \sum_{i = 1}^{n}\sum_{j = 1}^{n}lcm(i, j)^{gcd(i, j)} i=1nj=1nlcm(i,j)gcd(i,j)
1 ≤ n , m ≤ 5 × 1 0 5 1 \leq n,m \leq 5 \times 10^5 1n,m5×105

[bzoj3309]DZY Loves Math

点这里
题意:
对于正整数 n n n,定义 f ( n ) f(n) f(n) n n n 所含质因子的的最大幂指数。特别的 f ( 1 ) = 0 f(1) = 0 f(1)=0
T T T 组询问,每次给定 a , b a,b a,b,求 ∑ i = 1 a ∑ j = 1 b f ( g c d ( i , j ) ) \sum_{i = 1}^{a}\sum_{j = 1}^{b}f(gcd(i, j)) i=1aj=1bf(gcd(i,j))
1 ≤ T ≤ 1 0 4 , 1 ≤ a , b ≤ 1 0 7 1 \leq T \leq 10^4,1\leq a,b \leq 10^7 1T104,1a,b107

[bzoj4816–Sdoi2017] 数字表格

点这里
题意:
f ( n ) f(n) f(n) 表示斐波那契第 n n n 项。
T T T 组询问,每次给定 n , m n,m n,m。求 ∏ i = 1 n ∏ j = 1 m f [ g c d ( i , j ) ] \prod_{i = 1}^{n}\prod_{j = 1}^{m}f[gcd(i, j)] i=1nj=1mf[gcd(i,j)]。答案对 1 0 9 + 7 10^9 + 7 109+7 取模。
1 ≤ T ≤ 1000 , 1 ≤ n , m ≤ 1 0 6 1 \leq T \leq 1000,1\leq n, m \leq 10^6 1T1000,1n,m106

[hdoj4746]Mophues

点这里
题意:
Q Q Q 组询问,每次给你 n , m , p n, m, p n,m,p。求 ∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) ≤ p ] \sum_{i = 1}^{n} \sum_{j = 1}^{m}[gcd(i, j) \leq p] i=1nj=1m[gcd(i,j)p]
1 ≤ Q ≤ 5 × 1 0 3 , 2 ≤ n , m , p ≤ 5 × 1 0 5 1 \leq Q \leq 5 \times 10^3, 2 \leq n,m,p \leq 5 \times 10^5 1Q5×103,2n,m,p5×105

技巧总结

  1. 遇到与 g c d gcd gcd 有关的式子可以考虑莫比乌斯反演
  2. 把枚举 g c d gcd gcd 的式子提前是常见思路
  3. 遇到需要二次分块或者 分母是两个变量乘积 的情况,可以考虑交换枚举顺序,先枚举乘积。
  4. 遇到对 g c d gcd gcd 的大小有限制的题目可以考虑离线,把 g c d gcd gcd 由小到大依次加入。
  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值