欧拉函数

今天我们来学一下欧拉函数。

定义:欧拉函数是小于n的数中与n互质的数的数目。例如φ(8)=4,因为1,3,5,7均和8互质。

欧拉函数的通式为:,其中pi表示x的质因数。特别声明,φ(1)=1。

注意:每种质因数只一个。比如12=2*2*3那么φ(12)=12*(1-1/2)*(1-1/3)=4。

其中还有比较特殊的几个。

1:如果x是a^k,那么----除a的倍数外都与他互质。

2.如果n是奇数,那么.

3.如果n是质数,那么,即所有小于n的数都与他互质。

证明这个坑先留着吧,以后再来填。

计算时phi(x)=x(1-1/p1).....(1-1/pi)中的x(1-1/pi)可以转化为x/pi*(pi-1)用来避免小数的产生,同时先除后乘可以避免溢出。

对于单个数x的欧拉函数phi(x)计算方法如下:

从2开始枚举n是否为x的因数,如果是,则ans=ans/n*(n-1),然后一直除n。因为n是x的因数,所以也不会有相除后变成小数的情况发生.

一直枚举到x被除到了1说明已经除完了。

单个数的欧拉函数实现代码:

int Euler(int x){
	int res = x,now = 2;
	while(x>1){
		if(x%now==0){
			res/=now;
			res*=(now-1);
			while(x%now==0)x/=now;
		}
		now++;
	}
	return res;
}

和质数的筛法类似,欧拉函数也有一个复杂度是线性的筛,方法如下:

先将每个数对应的euler值euler[i]初始化为自己,接着从2开始枚举i<=,对所有小于maxn的i的倍数ki都进行一次euler[ki]=euler[ki]/i*(i-1)操作即可.

线性欧拉函数实现代码:

#define maxn 3000001			//可控制筛选范围1-maxn 

int euler[maxn];

void euler_init(){ 
    euler[1]=1;
    for(int i=2;i<maxn;i++)
      euler[i]=i;			//初始化 
    for(int i=2;i<maxn;i++)
       if(euler[i]==i)		//判断是否为质数 若euler[i]!=i说明已经进行过运算了 
          for(int j=i;j<maxn;j+=i)
            euler[j]=euler[j]/i*(i-1);//先进行除法是为了防止中间数据的溢出 
}
光说不做假把式
题目练习:

HDU2824 链接  http://acm.hdu.edu.cn/showproblem.php?pid=2824

题意很简单,有给定区间范围内欧拉函数值的和,区间不会超出(2,3000000) .

思路:先线性筛一遍,接着用一个数组存1-n的和,那么每次询问[l,r]时直接输出sum[r]-sum[l-1]即可。

注意要用long long存。但是开2个数组一个数组存欧拉函数值一个数组存和会爆内存,所以需要优化一下,直接将前面的和叠加在存放在欧拉函数值的数组里面就可以省一个数组。

代码:

//************************************************************************//
//*Author : Handsome How                                                 *//
//************************************************************************//
#include <bits/stdc++.h>
#define Max 3000001
typedef long long ll;
using namespace std;
ll euler[Max];
void Init(){ 
    euler[1]=1;
    for(int i=2;i<Max;i++)
      euler[i]=i;
    for(int i=2;i<Max;i++)
       if(euler[i]==i)
          for(int j=i;j<Max;j+=i)
            euler[j]=euler[j]/i*(i-1);
			//先进行除法是为了防止中间数据的溢出 
    for(int i=2;i<Max;i++)euler[i]+=euler[i-1];
	//将[1,i-1]的和叠加到euler[i]上可以节省一个数组 
}
int main()
{
    Init();
    int a,b;
    while(~scanf("%d %d",&a,&b))printf("%I64d\n",euler[b]-euler[a-1]);
    return 0;
}

下面是一个关乎欧拉函数的题。

界面熊学数学I

Time Limit:1000MS  Memory Limit:65536K
Total Submit:26 Accepted:2

Description

最近界面熊在学习快速幂时遇到了个难题,他很想知道的结果,可是一旦n很大时,快速幂也无能为力了。你能帮助界面熊算出结果吗?

Input

多组测试数据 
第一行为测试组数T (T≤20000), 
接下来的T行每行包括两个整数 n 和 mod,(0≤n≤100 , 1≤mod≤100).

Output

每行输出上述算式的结果

Sample Input

2
0 7
1 55

Sample Output

6
16
题意很简单,一个幂函数求余,但是指数比较大,不能用快速幂解决,这里用一个数论的公式:

  

其中是c的欧拉函数。有了这个数论知识我们就可以把这个题搞出来了,注意公式的要求,x>=phi(c),所以如果n比较小我们直接快速幂取模就好了。

我们注意到n和mod都比较小,测速数据2W组,我们先把欧拉函数筛一下比较快。

代码:

//************************************************************************//
//*Author : Handsome How                                                 *//
//************************************************************************//
#include <bits/stdc++.h>
#define fur(i,a,b) for(int i=(a);i<=(b);i++)
#define furr(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long ll;
inline void gn(long long&x){
	int sg=1;char c;while(((c=getchar())<'0'||c>'9')&&c!='-');c=='-'?(sg=-1,x=0):(x=c-'0');
	while((c=getchar())>='0'&&c<='9')x=x*10+c-'0';x*=sg;
}
inline void gn(int&x){long long t;gn(t);x=t;}
inline void gn(unsigned long long&x){long long t;gn(t);x=t;}
inline void gn(double&x){double t;scanf("%lf",&t);x=t;}
inline void gn(long double&x){double t;scanf("%lf",&t);x=t;}
//----------------------------------------------------------
const int maxn = 100+5;
int euler[maxn+5];
void Euler(){
	fur(i,1,maxn) euler[i] = i;
	fur(i,2,maxn)
 	   if(euler[i]==i)
		for(int j = i;j<100;j+=i)
		    euler[j] = euler[j]/i*(i-1);
}

int powmod(int a,int x,int mod){
	int ret = 1;
	while(x){
		if(x&1)
			ret = ret * a % mod;
        a = a * a % mod;
        x>>=1;
	}
	return ret;
}

int main()
{
	Euler();
	int T;
	gn(T);
	int ans;
	while(T--){
		int n,md;
		gn(n);
		gn(md);
		if(n<=3){			//n比较小直接快速幂取模 
			int x = 1;
			fur(i,1,n)x*=6;
			ans = powmod(6,x,md);
		}
		else{
		    int x = powmod(6,n,euler[md]) + euler[md];
		    ans = powmod(6,x,md);
		}
		printf("%d\n",ans);
	}
	return 0;
}



未完待续...



  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值