【数论】再谈线性筛(莫比乌斯反演)

须知

此文是作者写线性筛的第二篇文章,再读之前最好对于线性筛有一定的基础和认识,这里是作者写线性筛的第一篇文章。
前置知识

这里有一些函数定义:

  1. φ ( n ) \varphi(n) φ(n)
  2. d ( n ) d(n) d(n)
  3. ( n ) (n) (n)

基础题(复习)

F ( n ) = ∑ i = 1 n n gcd ⁡ ( i , n ) F(n)=\sum_{i=1}^n\frac{n}{\gcd(i,n)} F(n)=i=1ngcd(i,n)n
输入格式:

  • 第一行,一个整数 T T T。表示有 T T T 组测试数据。
  • 接下来有 T T T 行,每行一个整数 n n n
    全部数据:
  • 1 ≤ T ≤ 1 0 6 , 1 ≤ n ≤ 1 0 7 1\le T\le10^6,1\le n\le10^7 1T106,1n107
    输出格式:
  • T T T 行,每行一个整数。

输入/输出例子
输入:

10
39
12
37
15
91
63
32
87
28
7

输出:

1099
77
1333
147
6751
2623
683
5691
473
43

样例解释

分析
考虑枚举 gcd ⁡ ( i , n ) \gcd(i,n) gcd(i,n),则:
F ( n ) = ∑ d ∣ n n d ∑ i = 1 n [ gcd ⁡ ( i , n ) = d ] = ∑ d ∣ n n d ∑ i = 1 n d [ gcd ⁡ ( i , n d ) = 1 ] = ∑ d ∣ n n d φ ( n d ) = ∑ d ∣ n d φ ( d ) \large{F(n)=\sum_{d|n}\frac{n}{d}\sum_{i=1}^n[\gcd(i,n)=d]=\sum_{d|n}\frac{n}{d}\sum_{i=1}^\frac{n}{d}[\gcd(i,\frac{n}{d})=1]=\sum_{d|n}\frac{n}{d}\varphi(\frac{n}{d})=\sum_{d|n}d\varphi(d)} F(n)=dndni=1n[gcd(i,n)=d]=dndni=1dn[gcd(i,dn)=1]=dndnφ(dn)=dndφ(d)
F ( n ) F(n) F(n)为积性函数
因为 φ ( n ) \varphi(n) φ(n) 为积性函数,所以显然 F ( n ) F(n) F(n)也是。
线性筛

  1. f ( 1 ) = 1 f(1)=1 f(1)=1
  2. n n n 是质数,则 f ( n ) = 1 + ∑ i = 1 n − 1 n = n ( n − 1 ) + 1 f(n)=1+\sum_{i=1}^{n-1}n=n(n-1)+1 f(n)=1+i=1n1n=n(n1)+1
  3. q q q 是质数且 q ∤ n q\nmid n qn,则 f ( q n ) = f ( q ) f ( n ) f(qn)=f(q)f(n) f(qn)=f(q)f(n)
  4. q q q 是质数且 q ∣ n q\mid n qn,则: { q n = p k → f ( q n ) = f ( n ) + q n φ ( q n ) q n ≠ p k → f ( q n ) = f ( x ) f ( q n x ) , 其中 x 为最小质因子的最高次幂的值 \left\{\begin{matrix} qn=p^k\rightarrow f(qn)=f(n)+qn\varphi(qn) \\ qn\ne p^k\rightarrow f(qn)=f(x)f(\frac{qn}{x}),\text{其中}x\text{为最小质因子的最高次幂的值} \end{matrix}\right. {qn=pkf(qn)=f(n)+qnφ(qn)qn=pkf(qn)=f(x)f(xqn),其中x为最小质因子的最高次幂的值

参考代码:

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;
const int N=1e7+1,M=2e6+1,mod=1e9+7;
int t,n,m,cnt; 
struct fy{
	int prv[N],phi[N],f[N],mx[N];
	bool pr[N];
	int getphi(int x){
		int res=x;
		for(int i=2;i*i<=x;i++){
			if(x%i==0)
				res=res*(i-1)/i;
			while(x%i==0)
				x/=i;
		}
		if(x>1)
			res=res*(x-1)/x;
		return res;
	}
	void ola(int x){
		pr[1]=phi[1]=f[1]=1;
		for(int i=1;i<=x;i++){
			if(!pr[i])
				prv[++cnt]=i,phi[i]=i-1,f[i]=i*(i-1)+1,mx[i]=i;
			for(int j=1;j<=cnt&&i*prv[j]<=x;j++){
				int u=i*prv[j];
				pr[u]=1;
				if(i%prv[j]==0){
					phi[u]=phi[i]*prv[j];
					mx[u]=mx[i]*prv[j];
					f[u]=(mx[u]==u?f[i]+phi[u]*u:f[mx[u]]*f[u/mx[u]]);
					break;
				}
				else{
					phi[u]=phi[i]*(prv[j]-1);
					mx[u]=prv[j];
					f[u]=f[i]*f[prv[j]];
				}
			}
		}
	}
}A;
signed main(){
	IOS;
	A.ola(N-1);
	cin>>t;
	while(t--)
		cin>>n,cout<<A.f[n]<<"\n";
	return 0;
}

莫比乌斯反演

莫反我自认为还是一个神奇的东西,本文对其的证明可能不好,如有错误请指出。

莫比乌斯函数

定义

考虑两个函数 f ( x ) , g ( x ) f(x),g(x) f(x),g(x) 满足 f ( n ) = ∑ d ∣ n g ( d ) f(n)=\sum_{d|n}g(d) f(n)=dng(d)
易证,一定存在某个函数 h ( x ) h(x) h(x),使得 g ( n ) = ∑ d ∣ n f ( d ) h ( d ) g(n)=\sum_{d|n}f(d)h(d) g(n)=dnf(d)h(d)
然后我们发现,对于一个定值 d d d,如果 n n n 不同, h ( d ) h(d) h(d) 的值也不同。(如 g 1 = f 1 , g 2 = f 2 − f 1 g_1=f_1,g_2=f_2-f_1 g1=f1,g2=f2f1
但是呢, h ( d ) h(d) h(d) 的值与 n d \frac{n}{d} dn 有关,所以我们可以把 h ( d ) h(d) h(d) 看成 μ ( n d ) \mu(\frac{n}{d}) μ(dn),即 h ( d ) = μ ( n d ) h(d)=\mu(\frac{n}{d}) h(d)=μ(dn),其中 μ \mu μ 为一个函数。
所以可得 g ( n ) = ∑ d ∣ n f ( d ) μ ( n d ) g(n)=\sum_{d|n}f(d)\mu(\frac{n}{d}) g(n)=dnf(d)μ(dn)


这个 μ \mu μ 就是莫比乌斯函数了。
它是这么定义的:
μ ( x ) = { 1 x = 1 0 ∃ p ∈ P r i m e , p 2 ∣ x ( − 1 ) k x = ∏ i = 1 k p i ( 质因数分解 ) \mu(x)=\left\{\begin{matrix} 1 & x=1\\ 0 & \exists p\in Prime,p^2|x\\ (-1)^k & x=\prod_{i=1}^kp_i(\text{质因数分解} ) \end{matrix}\right. μ(x)= 10(1)kx=1pPrime,p2xx=i=1kpi(质因数分解)

性质

  1. μ \mu μ 是积性函数。
  2. ∀ n ∈ N + \forall n\in N_+ nN+ ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}\mu(d)=[n=1] dnμ(d)=[n=1]

证明:
1 :

  • x , y x,y x,y 中有一个 1 1 1,易证。
  • μ ( x ) , μ ( y ) \mu(x),\mu(y) μ(x),μ(y) 中有一个 0 0 0,易证。
  • 否则 μ ( x y ) = ( − 1 ) c n t x ( − 1 ) c n t y = μ ( x ) μ ( y ) \mu(xy)=(-1)^{cnt_x}(-1)^{cnt_y}=\mu(x)\mu(y) μ(xy)=(1)cntx(1)cnty=μ(x)μ(y)

2 :

  • n = 1 n=1 n=1,易证。
  • 否则设 n = ∏ i = 1 q p i x i n=\prod_{i=1}^qp_i^{x_i} n=i=1qpixi,根据 μ \mu μ 的定义,我们只考虑含有每个质因数一次的 k ( k ∣ n ) k(k|n) k(kn),算 k k k 的贡献。即: ∑ d ∣ n μ ( d ) = μ ( 1 ) + ∑ i = 1 q C q i ( − 1 ) i 1 q − i = C q 0 ( − 1 ) 0 1 q + ∑ i = 1 q C q i ( − 1 ) i 1 q − i = ∑ i = 0 q C q i ( − 1 ) i 1 q − i = ( − 1 + 1 ) q = 0 \sum_{d|n}\mu(d)=\mu(1)+\sum_{i=1}^qC_q^i(-1)^i1^{q-i}=C_q^0(-1)^01^q+\sum_{i=1}^qC_q^i(-1)^i1^{q-i}=\sum_{i=0}^qC_q^i(-1)^i1^{q-i}=(-1+1)^q=0 dnμ(d)=μ(1)+i=1qCqi(1)i1qi=Cq0(1)01q+i=1qCqi(1)i1qi=i=0qCqi(1)i1qi=(1+1)q=0

线性筛

其实根据定义就可以筛了。
参考代码:

void ola(int x){
	pr[1]=mu[1]=1;
	for(int i=2;i<=x;i++){
		if(!pr[i])
			prv[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&i*prv[j]<=x;j++){
			int u=i*prv[j];
			pr[u]=1;
			if(i%prv[j]==0){
				mu[u]=0;
				break;
			}
			else{
				mu[u]=-mu[i];
			}
		}
	}	
}

狄利克雷卷积

狄利克雷卷积可以理解为一种定义新运算,符号是 ∗ *
考虑两个函数 f ( x ) , g ( x ) f(x),g(x) f(x),g(x),则 ( f ∗ g ) ( x ) = ∑ d ∣ x f ( d ) g ( x d ) (f*g)(x)=\sum_{d|x}f(d)g(\frac{x}{d}) (fg)(x)=dxf(d)g(dx)
这就是狄利克雷卷积,其中狄利克雷卷积包含以下性质:

  1. a ∗ b = b ∗ a a*b=b*a ab=ba
  2. a ∗ b ∗ c = a ∗ ( b ∗ c ) a*b*c=a*(b*c) abc=a(bc)

整除分块

例:
∑ k = 1 n ⌊ n k ⌋ \sum_{k=1}^n \left \lfloor \frac{n}{k} \right \rfloor k=1nkn
直接上代码了,请自行理解一下啊。
Code:

for(int l=1,r;l<=n;l=r+1){
	r=(n/(n/l));
	ans+=(r-l+1)*(n/l);
}

时间复杂度 O ( n ) {\color{red}\text{时间复杂度} O(\sqrt n)} 时间复杂度O(n )

莫比乌斯反演

考虑两个函数 f ( x ) , g ( x ) f(x),g(x) f(x),g(x) 满足 f ( n ) = ∑ d ∣ n g ( d ) f(n)=\sum_{d|n}g(d) f(n)=dng(d),则 g ( n ) = ∑ d ∣ n f ( d ) μ ( n d ) g(n)=\sum_{d|n}f(d)\mu(\frac{n}{d}) g(n)=dnf(d)μ(dn)
正确性证明:
定义函数: I ( n ) = 1 , ϵ ( n ) = [ n = 1 ] , ∗ I(n)=1,\epsilon(n)=[n=1],* I(n)=1,ϵ(n)=[n=1],为狄利克雷卷积符号
∴ μ ∗ I = ϵ , h ∗ ϵ = h \therefore \mu*I=\epsilon,h*\epsilon=h μI=ϵ,hϵ=h
∵ f = g ∗ I \because f=g*I f=gI
∴ f ∗ μ = g ∗ I ∗ μ → f ∗ μ = g ∗ ( I ∗ μ ) → g ∗ ϵ = f ∗ μ → g = f ∗ μ \therefore f*\mu=g*I*\mu\rightarrow f*\mu=g*(I*\mu)\rightarrow g*\epsilon=f*\mu\rightarrow g=f*\mu fμ=gIμfμ=g(Iμ)gϵ=fμg=fμ
得证

例题

题目
题意:给定 n , m , k n,m,k n,m,k,求 ∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = k ] \sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k] i=1nj=1m[gcd(i,j)=k]
n = ⌊ n k ⌋ , m = ⌊ m k ⌋ n=\left \lfloor \frac{n}{k} \right \rfloor,m=\left \lfloor \frac{m}{k} \right \rfloor n=kn,m=km
a n s = ∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = 1 ] ans=\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=1] ans=i=1nj=1m[gcd(i,j)=1]

直接莫反:
∑ i = 1 n ∑ j = 1 m [ gcd ⁡ ( i , j ) = 1 ] = ∑ i = 1 n ∑ j = 1 m ∑ d ∣ gcd ⁡ ( i , j ) μ ( d ) = ∑ i = 1 n ∑ j = 1 m ∑ d = 1 min ⁡ ( n , m ) μ ( d ) [ d ∣ i ] [ d ∣ j ] = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ∑ i = 1 n [ d ∣ i ] ∑ j = 1 m [ d ∣ j ] = ∑ d = 1 min ⁡ ( n , m ) μ ( d ) ⌊ n d ⌋ ⌊ m d ⌋ \sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=1]=\sum_{i=1}^n\sum_{j=1}^m\sum_{d|\gcd(i,j)}\mu(d)=\sum_{i=1}^n\sum_{j=1}^m\sum_{d=1}^{\min(n,m)}\mu(d)[d|i][d|j]\\=\sum_{d=1}^{\min(n,m)}\mu(d)\sum_{i=1}^n[d|i]\sum_{j=1}^m[d|j]=\sum_{d=1}^{\min(n,m)}\mu(d)\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor i=1nj=1m[gcd(i,j)=1]=i=1nj=1mdgcd(i,j)μ(d)=i=1nj=1md=1min(n,m)μ(d)[di][dj]=d=1min(n,m)μ(d)i=1n[di]j=1m[dj]=d=1min(n,m)μ(d)dndm

那么我们就可以 O ( n ) O(n) O(n) 求答案了,但还不够,考虑用整除分块以及前缀和优化,最终复杂度 O ( n ) O(\sqrt n) O(n )
Code:

#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;
const int N=2e6+1,M=2e6+1;
int t,n,m,k,ans=0;
struct fy{
	int prv[N],cnt,mu[N],sum[N];
	bool pr[N];
	void ola(int x){
		pr[1]=mu[1]=1;
		for(int i=2;i<=x;i++){
			if(!pr[i])
				prv[++cnt]=i,mu[i]=-1;
			for(int j=1;j<=cnt&&i*prv[j]<=x;j++){
				int u=i*prv[j];
				pr[u]=1;
				if(i%prv[j]==0){
					mu[u]=0;
					break;
				}
				else{
					mu[u]=-mu[i];
				}
			}
		}
		for(int i=1;i<=x;i++)
			sum[i]=sum[i-1]+mu[i];
			
	}
}A;
signed main(){
	IOS;
	A.ola(N-1);
	cin>>t;
	while(t--){
		cin>>n>>m>>k;
		n/=k,m/=k;
		ans=0;
		for(int l=1,r;l<=min(n,m);l=r+1){
			r=min(n/(n/l),m/(m/l));//
			ans+=(A.sum[r]-A.sum[l-1])*(n/l)*(m/l);
		}
		cout<<ans<<"\n";
	}
	return 0;
}

注意一些细节。

  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值