莫比乌斯反演

前置知识

莫比乌斯反演的前置知识比较多,但比较碎,这里笔者当做回顾简单而已,如有莫比乌斯反演的需求,请跳过前置知识。 ,笔者也是一个初学者,如有错误,及时指出。感谢。

线性筛也是要会的。用来筛 \(\varphi , \mu\)

数论(整除)分块

用于加速统计答案的。 可以参考 \(LB\) 学长的博客 整除分块

我们发现在一个范围内 \(i \in [l , r]\) 使得 \(\lfloor \frac{k}{i} \rfloor\)\(k\) 为定值得时候,其也为定值。那么如果我们将其 \(O(n)\) 的去加,我们显然复杂度可能是承受不了,那么我们可以选择分块来解决,也就是将使得 \(\lfloor\frac{k}{i}\rfloor\) 为定值的一段区间 \(i\) 分成一块来简化复杂度 。
\(Code\)

//注释是二维 
for(qwq int l = 1 , r ; l <= n ; l = r + 1)  // for(qwq int l = 1  , r ; l <= std::min(n , m) ; l = r + 1)
{
	r = n / (n / l) ; // r = std::min(n / ( n / l ) , m / (m / l)) ; 
	···· 
}

欧拉函数 & 莫比乌斯函数

  • \(\varphi(n)\) 表示的是在 \(1\to n\)\(n\) 互质的个数 。 对于具体的欧拉函数,欧拉定理,不做阐述。
  • \(\mu(n)\) 由莫比乌斯这个人提出的。 有 \(3\) 条如下的性质。

\(1.\) \(\mu(1) = 1\)
\(2.\)\(n = \prod_{i = 1}^{k} p_i\) 其中 \(p_i\) 为质数并且每一项的指数为 \(1\) ,则 \(\mu(n) = (-1)^k\)
\(3.\) 其他情况为 \(0\)

求解 \(\mu\) 函数

void get_mu(int n) { // 1 -> n 的 mu 值 
	mu[1] = 1 ; 
	for(qwq int i = 2 ; i <= n ; i++) 
	{
		if(!vis) prime[++cnt] = i , mu[i] = - 1 ;
		for(qwq int j = 1 ; j <= cnt && i * prime[j] <= n ; j++) 
		{
			vis[i * prime[j]] = true ; 
			if(i % prime[j]) == 0) {mu[i * prime[j]] = 0 ; break ;} 
			else mu[i * prime[j]] = - mu[i] ; 	
		}
	}
}

积性函数

  • 对于两个数 \(n,m\) 互质且 \(f(n\times m) = f(n)\times f(m)\) 则称 \(f(x)\) 为积性函数。

  • 还有就是任意两个数 \(n,m\)\(f(n\times m) = f(n)\times f(m)\) 则称 \(f(x)\) 为完全积性函数。

举个例子 : \(\varphi(n)\) 就是一个积性函数, \(\mu(n)\) 也是一个积性函数。这里给出 \(\mu(n)\) 为积性函数的证明:

\(1.\) 根据上文的 \(\mu\) 的性质 \(1,3\) 的情况,这显然的是情况的。
\(2.\) 对于上文的 \(\mu\) 的性质 \(2\) 的情况 (笔者的证明显然不是特别的严谨)
对于一个数 \(n,m\) 我们将其分解为 \(n = \prod_{i = 1}^{k_1} p_i , m = \prod_{j = 1}^{k_2}q_i\) (其中 \(p_i , q_i\)) 均为质数 。 则 \(f(n) = (-1)^{k_1} , f(m) = (-1)^{k_2} , f(n\times m) = (-1)^{k_1 + k_2} = (-1)^{k_1} \times (-1)^{k_2} = f(n) \times f(m)\) ,则得证 。

狄里克雷卷积

定义两个数论函数 \(f,g\) 的卷积为 :

\[(f \ast g)(n) = \sum_{d|n} f(d) g(\frac{n}{d}) \]

三个性质和恒等式

  • \[f \ast g = g \ast f \]

  • \[(f \ast g) \ast h = f \ast (g \ast h) \]

  • \[(f + g) \ast h = f \ast h + g \ast h \]

\[我是来分开他们的 \]

\[\mu \ast I = \xi , \ \ \xi(n) = [n == 1] ,I(n) = n \]

\[\varphi \ast I = id \ \ (id(n) == n) \]

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

正文

其中可能结合博客 【化简式子】 食用效果更佳。

扔一个定理

对于 \(F(n)\)\(f(n)\) 是定义在非负整数集合上的两个函数,并且满足 \(F(n) = \sum_{d|n}f(d)\)

那么我们就得到结论:

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

证明:

\[\sum_{d|n}\mu(d)F(\frac{n}{d}) = \sum_{d|n}\mu(d)\sum_{d' |\frac{n}{d}}f(d') = \sum_{d'|n}f(d')\sum_{d | \frac{n}{d'}}\mu(d) = f(n) \]

这就是莫比乌斯反演定理。

P2568 GCD

【description】:

求解 \(\sum_{i = 1}^{n}\sum_{j = 1}^{m}[gcd(i , j)为质数]\)\(n \leq 10^7\)

【solution】:

我们开始化简式子:
我们是很容易得到 :

\[\sum_{p \in prime}\sum_{i = 1}^{n}\sum_{j = 1}^{m} gcd(i , j) = p \]

\[\sum_{p \in prime}\sum_{i = 1}^{\lfloor\frac{n}{p}\rfloor}\sum_{j = 1}^{\lfloor\frac{m}{p}\rfloor}gcd(i,j) = 1 \]

\[\sum_{p \in prime}\sum_{i = 1}^{\lfloor\frac{n}{p}\rfloor}\sum_{j = 1}^{\lfloor\frac{m}{p}\rfloor} \sum_{d | gcd(i,j)}\mu(p) \]

\[\sum_{p\in prime}\sum_{d = 1}^{n} \mu(p)\times \lfloor\frac{n}{dp}\rfloor\times \lfloor\frac{m}{dp}\rfloor \]

你以为这样就 \(OK\) 了,显然是不行的,我们枚举 \(p\) 大约是 \(10^5\)级别 再加上枚举 \(d\) 哪怕我们选择用数列分块,也逃不了 \(10^3\) ,我们还有 \(10^4\) 的级别的询问,总共应该是 \(10^{12}\) ,我们需要进一步的进行化简式子。

采取换元
我令 \(T = dp\) ,那么我们就将原式化成了

\[\sum_{p \in prime}\sum_{d = 1}^{\lfloor\frac{n}{p}\rfloor}\mu(d)\times\lfloor\frac{n}{T}\rfloor \times \lfloor\frac{m}{T}\rfloor \]

我们发现后两项跟 \(d, p\) 没有关系了,我们将其提出来就有:

\[\sum_{T= 1}^{n}\lfloor\frac{n}{T}\rfloor\times \lfloor\frac{m}{T}\rfloor\sum_{p\in prime \text{&}p|T} \mu(\frac{T}{p}) \]

后面的式子可以预处理出来,并且前面的式子可以用数列分块搞一下。

【Code】

/*
By : Zmonarch
知识点:
手握日月摘星辰,世间无我这般人!
*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <stack>
#include <iostream>
#define inf 2147483647
#define qwq register
//#define int long long
const int kmaxn = 1e7 + 10 ;
const int kmod = 998244353 ;
inline int read() {
	int x = 0 , f = 1 ; char ch = getchar() ;
	while(!isdigit(ch)) {if(ch == '-') f = - 1 ; ch = getchar() ;}
	while( isdigit(ch)) {x = x * 10 + ch - '0' ; ch = getchar() ;}
	return x * f ;
}
int cnt ; 
int mu[kmaxn] , prime[kmaxn] ; 
bool vis[kmaxn] ; 
long long sum[kmaxn] , f[kmaxn] ; 
void init(int n) {
	mu[1] = 1 ; 
	for(qwq int i = 2 ; i <= n ; i++)
	{
		if(!vis[i]) prime[++cnt] = i , mu[i] = - 1 ; 
		for(qwq int j = 1 ; j <= cnt && i * prime[j] <= n ; j++) 
		{
			vis[i * prime[j]] = true ; 
			if(i % prime[j] == 0) {mu[i * prime[j]] = 0 ; break ; } 
			else mu[i * prime[j]] = - mu[i] ;
		}
	}
	for(qwq int i = 1 ; i <= cnt ; i++) 
	 for(qwq int j = 1 ; j * prime[i] <= n ; j++) 
	  f[j * prime[i]] += mu[j] ; 
	for(qwq int i = 1 ; i <= n ; i++) 
	sum[i] = sum[i - 1] + f[i] ;
}
long long query(int n , int m) {
	long long ret = 0 ; 
	for(qwq int l = 1 , r ; l <= std::min(n , m) ; l = r + 1) 
	{
		r = std::min((n / (n / l)) , (m / (m / l))) ; 
		ret += (sum[r] - sum[l - 1]) * 1ll * (n / l) * 1ll * (m / l) ;
	}
	return ret ; 
}
signed main() {
	init(kmaxn - 10) ;  
	int T = read() ; 
	while(T--) 
	{
		int n = read() , m = read() ; 
		printf("%lld\n" , query(n , m)) ;
	} 
	return 0 ;
}

UVA11424 GCD - Extreme (I)

【description】 :

求解 \(\sum_{i = 1}^{n}\sum_{j = i + 1} ^{n} gcd(i , j)\)

多倍经验 : UVA11417 GCD,UVA11424 GCD - Extreme (I),P1390 公约数的和,P2398 GCD SUM,P2568 GCD,SP3871 GCDEX - GCD Extreme ,UVA11426 拿行李(极限版)

有的直接可过,有的略微改改就可过

【solution】:

化简式子 :

\[ans = \sum_{i = 1}^{n}\sum_{j = i + 1} ^{n}gcd(i,j) = \sum_{i=1}^{n}\sum_{j=i+1}^{n}Id(gcd(i,j)) \]

\[=\sum_{i=1}^{n}\sum_{j = i+1}^n (\varphi \ast I)(gcd(i,j)) = \sum_{i = 1}^{n}\sum_{j = i + 1}^{n}\sum_{d|gcd(i , j)}\varphi(d) \]

然后我们就能得到

\[\sum_d\sum_{i = 1}^{\lfloor\frac{n}{d}\rfloor}\sum_{j = i + 1}^{\lfloor\frac{n}{d}\rfloor}\varphi(d) \]

最后答案就为

\[ans = \sum_d \varphi(d) \times (2\times \lfloor\frac{n}{d}\rfloor - 1) \]

直接搞就行了。

【Code】

/*
By : Zmonarch
知识点: 
手握日月摘星辰,世间无我这般人! 
*/
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <stack>
#include <iostream>
#define inf 2147483647 
#define qwq register 
#define int long long 
const int kmaxn = 4e6 + 10 ; 
const int kmod = 998244353 ; 
inline int read() {
	int x = 0 , f = 1 ; char ch = getchar() ; 
	while(!isdigit(ch)) {if(ch == '-') f = - 1 ; ch = getchar() ;} 
	while( isdigit(ch)) {x = x * 10 + ch - '0' ; ch = getchar() ;}
	return x * f ; 
}
int cnt , k ; 
int prime[kmaxn] , phi[kmaxn] , sum[kmaxn]; 
bool vis[kmaxn] ; 
void eular(int n){
	phi[1] = 1 ; sum[1] = 1 ; 
	for(qwq int i = 2 ; i <= n ; i++) 
	{
		if(!vis[i]) phi[i] = i - 1 , prime[++cnt] = i ; 
		for(qwq int j = 1 ; j <= cnt && i * prime[j] <= n ; j++) 
		{
			vis[i * prime[j]] = true ; 
			if(i % prime[j] == 0) {phi[i * prime[j]] = phi[i] * prime[j] ; break ;} 
			else phi[i * prime[j]] = phi[i] * phi[prime[j]] ;
		}
		sum[i] = sum[i - 1] + phi[i] ; 
	}
}
void solve(int n) {
	int ret = 0 ; 
	for(qwq int l = 1 , r ; l <= n ; l = r + 1) 
	{
		r = n / (n / l) ; 
		ret += ( 1ll * (n / l) * (1ll * n / l - 1) / 2) * (sum[r] - sum[l - 1]) ;
	}
	printf("%lld\n" , ret) ;
}
signed main() {
	eular(kmaxn - 5) ; 
	while(1) 
	{
		int n = read() ; 
		if(!n) return 0 ; 
		solve(n) ; 	
	} 
	return 0 ; 
}

先放上

posted @ 2021-05-05 22:27  SkyFairy  阅读(78)  评论(0编辑  收藏  举报