欧拉函数与积性函数

目录

目录

一.欧拉函数

定义:

具体思路:

模板代码:

题目:

二.积性函数

题目:求欧拉函数

莫比乌斯与欧拉

[SDOI2008] 沙拉公主的困惑


一.欧拉函数

定义:

对正整数n,欧拉函数是小于n的正整数中与n互质的数的数目。

欧拉筛法就是求出小于等于n的所有素数的方法

具体思路:

找到一个素数后,就将它的倍数标记为合数,也就是把它的倍数“筛掉”;如果一个数没有被比它小的素数“筛掉”,那它就是素数。并且欧拉函数拥有线性的时间复杂度。

模板代码:

#include<iostream>
using namespace std;
const int N=1e8;
int p[N],cnt;
bool vis[N];
void ola(int n){
	vis[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]) p[++cnt]=i;
		for(int j=1;j<=cnt&&i*p[j]<=n;j++){
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
} 
int main(){
	ola(N);
	return 0;
}

注意:1.因为一般要筛的数字较大,所以vis数组可以使用bool类型,节省内存占用。

          2.“if(i%p[j]==0) break;”这一句是优化的关键,它可以保证每个数都是被自己最小的质因子筛            掉。

题目:

给定一个范围[1,n],有q次询问,每次输出第k小的素数。

思路:直接套欧拉筛法模板。注意:由于测试样例较多,输入输出使用scanf和printf.

#include<iostream>
using namespace std;
const int N=1e8;
int p[N],cnt;
int n,q,x;
bool vis[N];
void ola(int n){
	vis[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]) p[++cnt]=i;
		for(int j=1;j<=cnt&&i*p[j]<=n;j++){
			vis[i*p[j]]=1;
			if(i%p[j]==0) break;
		}
	}
} 
int main(){
	scanf("%d%d",&n,&q);
	ola(n);
	while(q--){
		scanf("%d",&x);
		printf("%d\n",p[x]);
	}
	return 0;
}

二.积性函数

积性函数

指对于所有互质的整数a和b有性质f(ab)=f(a)f(b)的数论函数。

单位函数:

f(n)=[n=1]

f(n)*f(m)=   1   n=1,m=1    =f(n,m)

                   0   else

莫比乌斯函数:

f(n)=  1     -->n=1

         -1    -->n=p1,p2.....pn

         0     -->else

题目:求欧拉函数

输入n,请输出ϕ(n)的值。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
int n;
int f(int n){
	int x=n;
	for(int i=2;i<=n/i;i++){
		if(n%i==0){
			x=x/i*(i-1);
			while(n%i==0) n/=i;
		}
	}
	if(n>1) x=x/n*(n-1);
	return x;
}
int main(){
	while(cin>>n&&n!=0){
		printf("%d\n",f(n));
	}
	return 0;
}

莫比乌斯与欧拉

题目描述:

给定n,T,每次在区间[1,n]中选一个数x,请输出μ(x)或ϕ(x)。

思路:

因为莫比乌斯函数是积性函数,所以可以结合欧拉筛对于每个素数直接求出值,对于其他数直接等于最小素数值的乘积

代码:

#include<iostream>
using namespace std;
const int N=1e6+5;
int n,t,q1[N],q[N],p[N],cnt x,y;
bool vis[N];
void ola(int n){
	vis[1]=1;
	q[1]=1;
	q1[1]=1;
	for(int i=2;i<=n;i++){
		if(!vis[i]){
			p[cnt++]=i;
			q[i]=-1;
			q1[i]=i-1;
		}
		for(int j=0;j<cnt&&p[j]*i<=n;j++){
			vis[p[j]*i]=1;
			if(i%p[j]==0){
				q[i*p[j]]=0;
				q1[i*p[j]]=q1[i]*p[j];
				break;
			}
			q[p[j]*i]=-q[i];
			q1[p[j]*i]=q1[p[j]]*q1[i];
		}
	}
}
int main(){
	scanf("%d%d",&n,&t);
	ola(n);
	while(t--){
		scanf("%d%d",&x,&y);
		if(x==1){
			printf("%d\n",q[y]);
		}else{
			printf("%d\n",q1[y]);
		}
	}
	return 0;
} 

[SDOI2008] 沙拉公主的困惑

题目描述:
现有钞票编号范围为 1 到 N 的阶乘,但是,只发行编号与M! 互质的钞票。计算出答案对 RR 取模后的结果即可。

思路:
首先证明一个定理,gcd(a,b)=gcd(a+kb,b)
设 d=gcd(a,b) ,那么我们可以将 a 和 b 表示为:


因为 d 带走了所有的 a 和 b 的公约数,所以 k1和 k2一定互质。


设 m|(k1+k·k2) 且 m|k2 ,可知 m|k·k2。由上节课的整除的第三条性质可知:


综合一下现在的线索:


可知 m=1 。也就是说 gcd(k1+k·k2,k2)=1 ,也就是说:


证毕。那么我们现在有了 gcd(a,b)=gcd(a+kb,b)这个条件,那么接下来尝试将小于等于M! 的与 M! 互质的数字与 1~N! 范围内的与 M! 互质的数字联系起来。
假设 x ≤ M! 并且 gcd( x , M! )=1 ,那么我们就知道了如下的结论:


在本题中,x+k·M!如果大于 N! 就失去了意义,那么 k 最大能是多少?


由于 x≤ M! ,所以我们得到 k 的取值范围:


也就是说,对于每一个 x ≤ M! 并且 gcd( x , M! )=1 的数字 x ,包括它自身的话,在1~N! 的范围内,x+k·M! 都和 M! 互质,一共有 N!/M! 个 x+k·M! 。那么有多少个这样的 x 呢?φ(M!) 个。所以最终要输出的结果为:


但是 φ(M!) 很难求,因为 M! 实在是太大了。注意,这里求欧拉函数时,不能直接求 φ(M! mod R ),也就是说 φ(x) mod R ≠ φ(x mod R) mod R 。举一个简单的例子,假设R=7:


类比上节课的【线性求逆元 2】和线性求阶乘的逆元,我们想到可以尝试找到一种递推关系。假设 M 是质数,那么我们可以想到,M 这个质数一定不可能是 (M-1)! 的质因数,也就是说 M 和 (M-1)! 互质。那么既然互质,欧拉函数是积性函数,那么我们可以得到:


如果 M 是合数呢?我们根据欧拉函数的计算式分析(以下 p 都代表某个质数):


相对于 (M-1)! ,M 并没有贡献新的质因数,所以:


所以可得:


总结一下:


利用这个递推式,我们就可以线性求出 φ(M!) 了!首先使用欧拉筛筛出所有的素数,然后线性递推。
本题中只用到了阶乘的欧拉函数,所以我们的欧拉筛只需要判素数即可,不需要计算欧拉函数值。递推的过程如下:

#include<iostream>
using namespace std;
void init(int n){
	fac[1]=phi_fac[1]=1;
	for(int i=2;i<=n;i++){
		fac[i]=fac[i-1]*i%R;
		if(vis[i]) phi_fac[i]=i*phi_fac[i-1]%R;
		else phi_fac[i]=(i-1)*phi_fac[i-1]%R;
	}
}
int main(){
	
	return 0;
}


fac 存逆元 ,phi_fac 存逆元的欧拉函数值,有了这些信息,我们在 main 函数中就能直接算出结果:

#include<iostream>
using namespace std;
int main(){
	scanf("%lld%lld",&n,&m);
	if(R<=n){
		printf("0\n");
		continue;
	}
	printf("%lld\n",fac[n]*qpow(fac[m],R-2,R)%R*phi_fac[m]%R);
	return 0;
}


写完发现,上面的代码并不能过本题,结果为答案错误。哪里出了问题?
我们想到,最终答案求的是:
所以在之前的代码中,如果 R < N ,可知 N! 一定是 R 的倍数,所以我们认为这个结果一定是 0 ,所以写了个特判。真的是这样吗?
有一种特殊情况,N!和 φ(M!) 中有 R 这个因子,M! 也有 R 这个因子,但是 N!φ(M!) 中的 R 的数量和 M! 中的 R 的数量相等,正好抵消掉了。
N!中 R 的数量一定大于等于 M!中 R 的数量。如果 N!和 φ(M!)中的 R 的数量和 M!中的 R的数量相等,那么结果和 R 无关。如果大于,那么直接输出 0 即可。因为大于的情况,结果一定是 R 的倍数,对 R 取余一定是 0.
大于的情况容易处理,相等的情况需要再做处理。因为在相等的情况中,结果和 R 无关。但是取余的时候是 R 的倍数会变成 0 ,导致答案不正确。所以我们的代码更改为:

#include<iostream>
using namespace std;
void init(int n){
	fac[1]=phi_fac[1]=1;
	for(int i=2;i<=n;i++){
		ll x=i;
		while(x%R==0) x/=R;
		fac[i]=fac[i-1]*x%R;
		x=(vis[i]?i:i-1);
		while(x%R==0) x/=R;
		phi_fac[i]=x*phi_fac[i-1]%R;
	}
}
int main(){
	
	return 0;
}
#include<iostream>
using namespace std;
int main(){
	scanf("%lld%lld",&n,&m);
	if(n/R>m%R){
		printf("0\n");
		continue;
	}
	printf("%lld\n",fac[n]*qpow(fac[m],R-2,R)%R*phi_fac[m]%R);
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值