蓝桥杯_数学知识_2 (欧拉函数 【用于欧拉定理】- 快速幂 【可求逆元】-扩展欧几里得算法【求解线性同余方程】)

欧拉函数

题目描述 :
给定 n 个正整数 ai,请你求出每个数的欧拉函数。
欧拉函数的定义
1~N 中与 N 互质的数的个数被称为欧拉函数,记为 ?(N)。
若在算数基本定理中,N=p1a1 * p2a2… pmam,则:
φ(N) = N×(p1-1)/p1×(p2-2)/p2×…×(pm-1)/pm
输入输出格式 :
输入
第一行包含整数 n。
接下来 n 行,每行包含一个正整数 ai。
输出
输出共 n 行,每行输出一个正整数 ai 的欧拉函数。
输入输出样例 :
输入
3
3
6
8
输出
2
2
4

定义:
给定n个正整数,请你求出每个数的欧拉函数
1~N中与N互质的数的个数为欧拉函数值φ(N)

N = p1a1 * p2*a2 * p3a3 … * pnan; 【先转换成分解质因数形式】
φ(N) = N * (1 - 1 / p1) * (1 - 1 / p2) * … * (1 - 1 / pn) 【与质因子的次数没有关系】
【证明:利用容斥原理结论】
1.从1~N中去掉p1 , p2 , … pk 的所有倍数 【会被去两次,如既是p1的倍数又是p2的倍数,所以要加回来】
2.加上所有pi * pj 的倍数 [i与j为1~k中任意两个数]
3.减去所有pi * pj * pk ;
4.加上所有pi * pj * pk * pl ;
1~N中所有与N互质的个数刚好为容斥原理公式: 减1加2减3加4…减N-1加N
N - N/p1 - N/p2- N/p3- N/p4 … - N / pn 为倍数个数
加 N / (p1
p2) + … + N / (p(n-1) * pn )
减N / (p1p2p3) …
加 N / (p1p2p3*p4)
== N * (1 - 1 / p1) * (1 - 1 / p2) * … * (1 - 1 / pn) == φ(N)

简记:欧拉函数φ(N)  ==  N * (1 - 1 / p1) * (1 - 1 / p2) * ... * (1 - 1 / pn) 
			 == N  /  pi  *(p1-1)

N × Π ( p i − 1 p i ) N× \Pi (\dfrac{pi-1}{pi}) N×Π(pipi1)


#include <iostream>
using namespace std; 
#include<algorithm> 

int main()
{
	int n;
	cin >> n;
	
	while(n --)
	{
		int a;
		cin >> a;
		
		int res = a;
		for(int i = 2;i <= a / i;i ++) //求质因数
			if(a % i == 0)
			{
				res = res / i * (i - 1); //欧拉函数公式
				while(a % i == 0) a /= i; //除去质因数的指数次 ,去除此质因数倍数
			}
			
		if(a > 1) res = res / a * (a - 1); //可能有> sqrt(a) 的质因数
	}
	
	return 0;
}

874. 线性筛法求欧拉函数

给定一个正整数n,求1~n中每个数的欧拉函数之和。
输入格式
共一行,包含一个整数n。
输出格式
共一行,包含一个整数,表示1~n中每个数的欧拉函数之和。
数据范围
1≤n≤106
输入样例:
6
输出样例:
12

分类讨论:

  1. i%pj == 0 φ(pj * i) == pj * φ(i) == i * ∑(1 - 1 / p[n])
  2. i%pj != 0 φ(pj * i) == pj * φ(i) * (1 - 1 / pj) == φ(i) * (pj - 1)

φ ( p j ∗ N ) = p j ∗ φ ( N ) ∗ ( p j − 1 p i ) 约分 = φ ( N ) ∗ ( P j − 1 ) φ(pj * N) = pj * φ(N) *(\dfrac{pj-1}{pi}) \dfrac{约分}{} = φ(N) * (Pj-1) φ(pjN)=pjφ(N)(pipj1)约分=φ(N)(Pj1)

//线性筛法 O(n) 【 能求很多... ,如下面就在 if(i % primes[j] == 0)语句变型分类判断 ,在求质因子的过程中顺带求出欧拉函数值 
/*
prime[cnt++]=i;:如果发现i是素数,那么就把它放进prime数组里,同时把计数器加一。
prime[j]<=n/i;如果pj乘以i的值大于n就判断完了,【选择成对约数小的 n/a | n - - ->    a < n^1/2^】。
内层for循环要做两件事情,分为两个条件判断:
1.如果i除以prime[j](简称pj)不等于0,说明pj不是i的最小质因子,并且可以说明pj比i的最小质因子小。
这样,pji的最小值因子就一定是pj,就先把pji这个数筛掉。
2.如果i除以pj等于0,说明pj是i的最小质因子,而我们如果继续把j++的话,那么下一个pj就一定不是当前i的最小质因子了,为了避免后面重复计算造成错误,所以这里要break。
综上所述,无论pj能否整除i,pj*i一定是合数,一定要筛掉。【约数成对出现】 

线性筛法相当于是对埃氏筛法求素数个数的一种优化,线性筛是用最小质因数进行素数筛选,而埃氏筛法则是通过每一个数的倍数进行素数筛选。比如6这个数,用线性筛法只会被筛一次,而用埃氏筛法会分别被2和3各筛一次。

这里引用别的思路2~~: 
用线性筛求质数个数的具体做法为 : 
在void getPrimes(int n)函数中,最外层依旧是从2遍历到n进行循环,st[i]表示数字i是否被筛除。
每一次循环开始时,若是if(!st[i]), 则primes[cnt ++ ] = i,表明i是个素数。
不管i是否为素数,我们都要再在for (int j = 0; primes[j] <= n / i ; j ++ )这个循环中,对其他数进行筛选。
先让st[primes[j] * i ]= true, 在此可以保证prime[j] 一定是prime[j] * i的最小质因数,
因为后面还有一行代码,if(i % primes[j] = = 0) break :
i % primes[j] == 0 表明primes[j]是i的最小质因数,
如果i % primes[j] != 0 ,prime[j]也一定小于i 的最小质因数,因为primes[j]是从大到小存储素数的。
至于这里为什么要进行break,因为当primes[j]成为i的最小质因数之后,
在下一次循环得到的primes[j + 1] 就不再是i * primes[j + 1] 的最小质因数了,故循环结束。


#include<bits/stdc++.h>
using namespace std;
const int N = 1000010;
typedef long long LL;
int primes[N],cnt; //质因子数组 ,统计质因子个数 
int phi[N]; //欧拉函数表  
bool st[N]; //标记数组 



LL get_eulers(int n)
{
	phi[1] = 1;//定义出发,1与自己互质
	for(int i = 2; i <= n;i++) //线性筛法  + phi代表欧拉函数符号
	{
		if(!st[i])
		{
			primes[cnt ++] = i; 
			phi[i] = i - 1;  //为质数,欧拉函数就为i-1
		}
		for(int j = 0; primes[j] <= n / i;j++) 
		{
			st[primes[j] * i] = true; //筛非质数 
			if(i % primes[j] == 0)     // 倍数遍历完 break;  
			{
				phi[primes[j] * i] = phi[i] * primes[j];  //==0记不用减1
				break;
			}
			phi[primes[j] * i] = phi[i] * (primes[j] - 1);
		}
	}
	
	LL res = 0; //1~n的欧拉函数之和
	for(int i = 1 ;i <= n ; i++) res += phi[i];
	return res;
	
}

int main()
{
	int n;
	cin >> n;
	
	cout << get_eulers(n) << endl;
	
	return 0;
}

欧拉函数用途:欧拉定理
欧拉定理: aφ(n) 与 n 互质 则 gcd(aφ(n) , n) = 1 或者 说 aφ(n) 与 n mod 1 同余
【学数学要完全自己想,能推出一遍公式,在背了很多熟练的基础上】
费马小定理:
ap-1 与 p 与1同模 == gcd(ap-1,p) = 1

875. 快速幂 O(logk)

给定 n 组 ai,bi,pi,对于每组数据,求出 aibi mod pi 的值。[ mod p 答案就在 1 ~ p -1 之中]
输入格式
第一行包含整数 n。
接下来 n 行,每行包含三个整数 ai,bi,pi。
输出格式
对于每组数据,输出一个结果,表示 aibi mod pi 的值。
每个结果占一行。
数据范围
1≤n≤100000,
1≤ai,bi,pi ≤2 ×109
输入样例:
2
3 2 5
4 3 9
输出样例:
4
1

快速幂 O(logk) 【反复平方法】
ak mod p
1 <= a,k,p <= 1e9
预处理: a20^^ mod p , a21^^ mod p ,a22^^ mod p ,
ak = ∑aij^^ == a2x0+2x1+…+2xn^^ ;
即把ak的拆成 a平方k次

简记:typedef long long LL;
			LL qmi(底数,幂,%p)
			while(k)
			{	
				if(k & 1)res = (LL)res * a % p;  //奇数次幂开始多乘1个a
				k >>= 1;     等效 k /= 2;   【 >>= 1   等效   /= 2 】
				a = (LL)a * a % p; 
			}



#include<algorithm>

typedef long long LL;//数论大多题long long ,读入数据也用scanf较好 

LL qmi(int a,int k,int p)
{
	int res = 1;
	while(k) //2^x^ (奇数+ 1) == k	
	{	//若指数为奇数,多乘一个a 
		if(k & 1)res = (LL)res * a % p;  //奇数次幂开始多乘1个a [也可以写成 k%2]【 if语句在k奇数即 k%2 == 1时执行 】  
		k >>= 1; //k /= 2;
		a = (LL)a * a % p; 
	}
	return res;
}

int main()
{
	int n;
	scanf("%d",&n);
	while(n --)
	{
		int a,k,p;
		scanf("%d%d%d",&a,&k,&p);
	
		printf("%lld\n",qmi(a,k,p));
	}

	return 0;
}

876. 快速幂求逆元

给定n组ai,pi,其中pi是质数,求ai模pi的乘法逆元,若逆元不存在则输出impossible。
注意:请返回在0~ p - 1 之间的逆元。
乘法逆元的定义
若整数b,m互质,并且b|a,则存在一个整数x,使得a/b≡ax(mod m),则称x为b的模m乘法逆元,记为b(-1) (mod m)。
b存在乘法逆元的充要条件是b与模数m互质。当模数m为质数时,b^(m?2)即为b的乘法逆元。
输入格式
第一行包含整数n。
接下来n行,每行包含一个数组ai,pi,数据保证pi是质数。
输出格式
输出共n行,每组数据输出一个结果,每个结果占一行。
若ai模pi的乘法逆元存在,则输出一个整数,表示逆元,否则输出impossible。
数据范围
1≤n≤105,
1≤ai,pi≤2
109
输入样例:
3
4 3
8 5
6 3
输出样例:
1
2
impossible

逆元:

a / b ≡ a * x (mod m)
a / b ≡ a * b逆元 (mod m)
两边同乘b ,a ≡ a * b * b逆元 (mod m)
消去a , b * b逆元 ≡ 1 (mod m) 【性质】
再由费马定理 : bp-1 ≡ 1 (mod p) - - - > 重要结论:b * b p-2 ≡ 1 (mod p) 【证明完毕】
【找到一个x,使得b*x 同余 1 (mod p) ,则可取 x = bp-2

费马小定理: 如果p是一个质数,而整数a不是p的倍数,则有a(p-1)≡1(mod p) 因此a mod p的逆元是 a(p-2)
求取a(p-2) 可以用快速幂求取
逆元能解决类似:(a / b mod p)类型的问题

简记:qmi(底数,次幂,mod p)    快速幂
			if(a % p)printf("%lld",res);    else puts("impossible");
			因为 a % p == 0时会为res = 1,不正确,应该为impossible ,所以用 a % p  



typedef long long LL;
#include<cstdio>

LL qmi(int a,int k,int p)
{
	LL res = 1; //初始值为1    !!! 
	while(k)
	{
		if(k & 1) res = (LL)res * a % p; //强制转换 
		k >>= 1;
		a = (LL)a * a % p;
	}
	return res;
}


int main()
{
	int n;
	scanf("%d",&n);
	while(n --)
	{
		int a,k,p;
		scanf("%d%d",&a,&p); //已知推导公式: a逆元求法:  a * a^p - 2 ^ ≡ 1 (mod p)   即k = p - 2  
		LL res = qmi(a,p-2,p);//a逆元 == a^p-2^ % p 
		if(a % p)printf("%lld",res); //因为 a % p == 0时会为res = 1,不正确,应该为impossible ,所以用 a % p  
		else puts("impossible");
	}
	
	return 0;
}

877. 扩展欧几里得算法

(递归,裴蜀定理,gcd)
给定 n 对正整数 ai,bi,对于每对数,求出一组 xi,yi,
使其满足 ai * xi+bi * yi=gcd(ai,bi)。
输入格式
第一行包含整数 n。
接下来 n 行,每行包含两个整数 ai,bi。
输出格式
输出共 n 行,对于每组 ai,bi,求出一组满足条件的 xi,yi,每组结果占一行。
本题答案不唯一,输出任意满足条件的 xi,yi 均可。
数据范围
1≤n≤105,
1≤ai,bi≤2×109
输入样例:
2
4 6
8 18
输出样例:
-1 1
-2 1

拓展欧几里得算法
时间复杂度:O(n*log(a+b))
①拓展欧几里得算法解决的问题:对于任意给定的两个正整数a、b,求解x,y使得ax+by=(a,b) (a,b)的意思:a和b的最大公约数
②问题引入:裴蜀定理
给定任意一对正整数a、b,存在非零整数x、y,使得ax+by=(a,b)
扩欧作用:求解方程 ax+by=gcd(a,b) 的解(x、y)、求逆元 等
推理:
①当 b=0 时 ax+by=a 所以 x=1,y=0
②当 b≠0 时
设gcd(a,b)=gcd(b,a%b)=d

by′+(a%b)x′=gcd(b,a%b)
by′+(a–a/b–b)*x’=gcd(b,a%b)
b(y’-[a/b]x’)+ax’=gcd(b,a%b)=gcd(a,b)=d
所以
x=x′(不变),y=y’ - [a/b]x’(更新)
要用scanf,这时候连cin解除同步都比它慢得多
//本题需要用子问题的结果来计算当前问题的结果,所以需要等子问题算完之后再算

裴蜀定理:
对任意一对正整数 a,b,那么一定存在非零整数x,y【构造法证明】,使得ax+by = gcd(a,b) 即a,b能凑出来的最小的正整数
构造法证明:即欧几里得算法:

通解
x = x0 - (b/a) * k , k∈Z
y = y0 + (b/a) * k , k∈Z




int gcd(int a,int b)
{
	if(b == 0)return a; //0和任何数的最大公约数都是那个数本身 
	return gcd(b,a%b);
	
}

int exgcd(int a,int b,int &x,int &y)    //扩展欧几里得算法 
{
	if(!b) //若b = 0 ,返回 x = 1,y = 0 
	{
		x = 1,y = 0;
		return a;		
	}	
	
	int d = exgcd(b,a%b,y,x); //把a,b颠倒了,x,y对应系数翻转 
	y -= a / b * x;
	return d;
} 



int main()
{
	int n;
	scanf("%d",&n);
	
	while(n --)
	{
		int a,b,x,y;//根据题意,让x,y引用传递  exgcd( int a,int b , int &x,int &y);
		scanf("%d%d",&a,&b);
		
		exgcd(a,b,x,y);
		
		printf("%d %d\n",x,y);
	}
	
	return 0;
} 

应用:求线性同余方程 ax 与 b 同余 (mod m)
给定n组数据ai,bi,mi,对于每组数求出一个xi,使其满足ai?xi≡bi(mod mi),如果无解则输出impossible。
输入格式
第一行包含整数n。
接下来n行,每行包含一组数据ai,bi,mi。
输出格式
输出共n行,每组数据输出一个整数表示一个满足条件的xi,如果无解则输出impossible。
每组数据结果占一行,结果可能不唯一,输出任意一个满足条件的结果均可。
输出答案必须在int范围之内。
数据范围
1≤n≤105,
1≤ai,bi,mi≤2?109
输入样例:
2
2 3 6
4 3 5
输出样例:
impossible
-3

exgcd求解线性同余方程
如果 a?x≡b(mod m)
那么存在 y∈Z 使得 a?x+m?y=b
令d=gcd(a,m)
exgcd(a,m,x0,y0) 得a?x0+m?y0=d
if(b % d == 0)则有解
等式两边同乘 b/d 得
b/d(a?x0+m?y0)=b
a?(b/d?x0)+m?(b/d?y0)=b
x=b/d?x0




int main()
{
	int n;
	scanf("%d",&n);
	
	while(n --)
	{
		
		int a,b,m;	
	
		scanf("%d%d%d",&a,&b,&m);
		int x,y;
		int d = exgcd(a,m,x,y);
		if(b % d) puts("impossible"); //d不是b的倍数,无解 
		else printf("%d\n",(LL)x * (b / d) % m); 
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值