莫比乌斯反演

0.前言

老年退役选手的消遣

1.莫比乌斯函数

(mu)或莫比乌斯函数是指以下函数:

[mu(n) = left{ egin{aligned} 1 quadqquadqquad qquad qquad qquad qquad n = 1 \ quad qquad (-1)^k qquad n = p_1 p_2...p_k, p_i为互不相同的素数 \ 0 quadqquadqquadqquad n含有指数大于1的质因子 end{aligned} ight. ]

莫比乌斯函数的性质:

性质1:对于任意正整数n:

[sum_{dmid n} mu(n) = left{ egin{aligned} 1 qquad n = 1 \ 0 qquad n > 1 end{aligned} ight. ]

性质2:对于任意正整数n:

[sum_{d mid n} frac{mu(d)}{d} = frac{phi(n)}{n} ]

性质2推论:

[sum_{d mid n} dcdot mu(frac{n}{d}) = phi(n) ]

证明: 令(k = frac{n}{d}),带入原式,易得。

线性筛求 (mu)

lld mu[N];
void euler(lld n) {
	lld p[N]; memset(p, 0, sizeof(p));
	bool vis[N]; memset(vis, 0, sizeof(vis));
	mu[1] = 1; int tot = 0;
	for(int i = 2; i <= n; i++) {
		if(!vis[i]) {
			p[++tot] = i; vis[i] = true;
			mu[i] = -1;
		}
		for(int j = 1; j <= tot && p[j] * i <= n; j++) {
			vis[p[j] * i] = true;
			if(i % p[j] == 0) mu[i * p[j]] = 0;
			else mu[i * p[j]] = mu[i] * (-1);
		}
	}
}

2.莫比乌斯反演

(f(n), F(n)) 为定义在正整数集合上的函数。
(F(n)) 满足:

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

则:

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

或:

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

前一个式子证明:(使用定义法)

[右 = sum_{d mid n} mu(d)F(frac{n}{d}) = sum_{d mid n}mu(d)cdot sum_{k mid frac{n}{d}}f(k) = sum_{k mid n} f(k) cdot sum_{d mid frac{n}{k}} mu(d) = f(n) = 左 ]

证毕。(第三个等号看不明白可以代个数算算,可以发现两边正好相等)
(也可以使用狄利克雷卷积证明)

3.题目:

[HAOI2011]Problem b

求:

[sum_{i = a}^{b} sum_{j = c}^{d}[gcd(i, j) = k] ]

利用二维前缀和将其转化为求

[sum_{i = 1}^{n} sum_{j = 1}^{m}[gcd(i, j) = k] ]

开始推式子:(式子中的除法均为整除)

[ egin{eqnarray*} && sum_{i = 1}^{n} sum_{j = 1}^{m}[gcd(i, j) = k] \ && = sum_{i = 1}^{ frac{n}{k} } sum_{j = 1}^{ frac{m}{k} }[gcd(i, j) = 1]\ && = sum_{i = 1}^{ frac{n}{k} } sum_{j = 1}^{ frac{m}{k} } sum_{d mid gcd(i, j)} mu(d) end{eqnarray*} ]

因为 (d mid gcd(i, j)) 可以表示为同时满足 (d mid i, d mid j) ,所以原式

[egin{eqnarray*} && = sum_{d = 1}^{min(frac{n}{k}, frac{m}{k})}mu(d) cdot sum_{d mid i}^{frac{n}{k}}sum_{d mid j}^{frac{m}{k}} 1\ && = sum_{d = 1}^{min(frac{n}{k}, frac{m}{k})}mu(d) frac{n}{kd}frac{m}{kd} end{eqnarray*} ]

至此,已经可以 (O(n)) 求解了。但题目中询问次数很大,所以需要降低每次询问的复杂度。
考虑整除分块,设当前一段区间的左端点为 (d) ,则右端点为 (min(n/(n / d), m / (m / d))), (mu(d)) 用前缀和实现。

typedef int lld;
const int N = 100005;
lld mu[N];
void euler(lld n) {
	lld p[N]; memset(p, 0, sizeof(p));
	bool vis[N]; memset(vis, 0, sizeof(vis));
	mu[1] = 1; int tot = 0;
	for(int i = 2; i <= n; i++) {
		if(!vis[i]) {
			p[++tot] = i; vis[i] = true;
			mu[i] = -1;
		}
		for(int j = 1; j <= tot && p[j] * i <= n; j++) {
			vis[p[j] * i] = true;
			if(i % p[j] == 0) mu[i * p[j]] = 0;
			else mu[i * p[j]] = mu[i] * (-1);
		}
	}
	for(int i = 1; i <= n; i++) {
		mu[i] += mu[i - 1];
	}
}
lld a[N], b[N], c[N], d[N], k[N];
lld t, f[N];
lld calc(lld n, lld m, lld k) {
	lld p = min(n / k, m / k), tmp = 0;
	for(lld i = 1; i <= p; ) {
		lld j = min(n / (n / i), m / (m / i));
		if(j > p) break;
		tmp = tmp + (mu[j] - mu[i - 1]) * (n / i / k) * (m / i / k);
		i = j + 1;
	}
	return tmp;
}
int main() {
//	freopen("data.in", "r", stdin);
	int n; scanf("%d", &n);
	for(int i = 1; i <= n; i++) {
		scanf("%d%d%d%d%d", &a[i], &b[i], &c[i], &d[i], &k[i]);
		t = max(t, max(b[i], d[i]));
	}
	euler(t);
	for(int i = 1; i <= n; i++) {
		printf("%d
", calc(b[i], d[i], k[i]) - calc(b[i], c[i] - 1, k[i]) - calc(a[i] - 1, d[i], k[i]) + calc(a[i] - 1, c[i] - 1, k[i]));
	}
	return 0;
}

此题开long long会暴毙。

原文地址:https://www.cnblogs.com/mcggvc/p/14284843.html