0
点赞
收藏
分享

微信扫一扫

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

蛇发女妖 2022-04-05 阅读 94

文章目录

欧拉函数




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

int test_01()
{
	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); //最后一个 ,因为是从i = 2开始判断质数 ,但公式到N 
	}
	
	return 0;
}

874. 筛法求欧拉函数




const int N = 1000010;
typedef long long LL;
int primes[N],cnt; //质因子数组 ,统计质因子个数 
int phi[N]; //欧拉函数 
bool st[N]; //标记数组 

//线性筛法 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] 的最小质因数了,故循环结束。



*/

LL get_eulers(int n)
{
	phi[1] = 1;//定义出发,1与自己互质,有点不符合公式 
	for(int i = 2; i <= n;i++)
	{
		if(!st[i])
		{
			primes[cnt ++] = i; //? 
			phi[i] = 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];
				break;
			}
			phi[primes[j] * i] = phi[i] * (primes[j] - 1);
		}
	}
	
	LL res = 0;
	for(int i = 1 ;i <= n ; i++) res += phi[i];
	return res;
	
}

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

875. 快速幂 O(logk)




#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;  //也可以写成 k%2 【 if语句在k奇数即 k%2 == 1时执行 】  
		k >>= 1; //k /= 2;
		a = (LL)a * a % p; 
	}
	return res;
}

int test_03()
{
	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. 快速幂求逆元

逆元:




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 test_04()
{
	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. 扩展欧几里得算法




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 test_05()
{
	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;
} 



int test_06()
{
	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;
}



int main() {
	
	test_06();
	
	return 0;

}

举报

相关推荐

0 条评论