莫比乌斯反演--「HAOI 2011」Problem b

简介: 对于一些函数\(f(n)\),如果很难直接求出他的值,而容易其倍数或约数和\(g(n)\),可以通过莫比乌斯反演简化问题

前置知识:

  • 数论分块

其实在之前的博客里,做余数求和的时候总结过,但是也忘得差不多啦

数论分块的过程:考虑求含有\(\left\lfloor \frac{n}{i} \right\rfloor\)的求和式子

对于任意一个\(i\)\((i\leq n)\),我们需要找到一个最大的\(j\)\((i\leq j \leq n)\),满足\(\left\lfloor \dfrac{n}{i}\right\rfloor = \left\lfloor \dfrac{n}{j}\right\rfloor\),此时\(j=\left\lfloor \frac{n}{\left\lfloor \frac{n}{i}\right\rfloor}\right\rfloor\)

这样我们可以把一些值相同的数分成一大块一起计算,是不是方便很多?

证明:

\(k = \left\lfloor \frac{n}{i}\right\rfloor\),考虑证明当\(\left\lfloor \frac{n}{j}\right\rfloor = k\)时,\(j\)的最大值为\(\left\lfloor \frac{n}{k}\right\rfloor\)

\(\left\lfloor \frac{n}{j}\right\rfloor = k \Longleftrightarrow k \leq \frac{n}{j}<k+1 \Longleftrightarrow \frac{1}{k+1} < \frac{j}{n}\leq \frac{1}{k} \Longleftrightarrow \frac{n}{k+1} < j \leq \frac{n}{k+1}\)

又因为\(j\)是整数,所以\(j_{max} = \left\lfloor \frac{n}{k} \right\rfloor\),这样我们每次以\([i,j]\)分块求和即可

  • 莫比乌斯函数

\(\mu\)为莫比乌斯函数,定义为:\(\mu (n) = \begin{cases}1&n=1\\0&n含有平方因子\\{(-1)}^k&k为n的本质不同的质因子个数\end{cases}\)

性质:

  1. \(\sum\limits_{d\mid n} \mu (d) = \begin{cases} 1&n = 1\\0&n \neq 1\end{cases}\)

  2. \([gcd(i,j)==1] \Longleftrightarrow \sum\limits_{d\mid gcd(i,j)} \mu (d)\)

code:

void init(){
	mo[1] = 1;
	for (int i = 2;i <= 1e6;i++){
		if (!death[i]) primelist[++tot] = i,mo[i] = -1;
		for (int j = 1;j <= tot&&i*primelist[j] <= 1e6;j++){
			death[i*primelist[j]] = 1;
			if (i%primelist[j] == 0){
				mo[i*primelist[j]] = 0;
				break;
			}
			mo[i*primelist[j]] = -mo[i];
		}
	}
	for (int i = 1;i <= 1e6;i++) mo[i] += mo[i-1];
}
  • 莫比乌斯反演

公式:

定义\(f(n)\)\(g(n)\)为两个数论函数

如果有\(f(n) = \sum\limits_{d\mid n}g(d)\),那么有\(g(n) = \sum\limits_{d\mid n} \mu (d)f(\frac{n}{d})\)
如果有\(f(n) = \sum\limits_{d\mid n}g(d)\),那么有\(g(n) = \sum\limits_{d\mid n} \mu (\frac{n}{d})f(d)\)

例题:

problem:

求符合\(gcd(x,y) = k\)\((x,y)\)的个数

solution:

\(\sum\limits_{i=1}^n\sum\limits_{j=1}^m [gcd(i,j) = k] \Longleftrightarrow \sum\limits_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum\limits_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor}[gcd(i,j) = 1] \Longleftrightarrow \sum\limits_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor}\sum\limits_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor} \sum\limits_{d\mid gcd(i,j)} \mu (d)\)

交换求和顺序,枚举\(d\mid gcd(i,j)\)即可

\(\sum\limits_{d=1} \mu (d)\sum\limits_{i=1}^{\left\lfloor\frac{n}{k}\right\rfloor} [d\mid i]\sum\limits_{j=1}^{\left\lfloor\frac{m}{k}\right\rfloor} [d\mid j]\Longleftrightarrow \sum\limits_{d=1} \mu (d)\left\lfloor \frac{n}{k}\right\rfloor\left\lfloor\frac{m}{k}\right\rfloor\)

code:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 5e4+10;
int n;
int a,b,c,d,k;
int death[maxn],primelist[maxn],tot,mu[maxn];
void init(){
	mu[1] = 1;
	for (int i = 2;i <= 50000;i++){
		if (!death[i]) primelist[++tot] = i,mu[i] = -1;
		for (int j = 1;j <= tot&&i*primelist[j] <= 50000;j++){
			death[i*primelist[j]] = 1;
			if (i%primelist[j] == 0){
				mu[i*primelist[j]] = 0;
				break;
			}
			mu[i*primelist[j]] = -mu[i];
		}
	}
	for (int i = 1;i <= 50000;i++) mu[i] += mu[i-1];
}
int solve(int n,int m){
	int res = 0;
	for (int l = 1,r;l <= min(n,m);l = r+1){
		r = min(n/(n/l),m/(m/l));
		res += (mu[r]-mu[l-1])*(n/l)*(m/l);
	}
	return res;
}
int main(){
	n = read();init();
	for (int i = 1;i <= n;i++){
		a = read(),b = read(),c = read(),d = read(),k = read();
		 printf("%d\n", solve(b/k,d/k)-solve(b/k,(c-1)/k)-solve((a-1)/k,d/k)+solve((a-1)/k,(c-1)/k));
	}
	return 0;
}
posted @ 2020-11-24 22:43  小又又yyyy  阅读(84)  评论(0编辑  收藏  举报