质数+约数+欧拉函数

一、质数

质数,合数:都是针对大于1的自然来说的。
质数:只包含1和本身两个约数。
1、判断质数(试除法)O(sqrt(n))
在枚举的时候只需要枚举到sqrt(n)即可,因为如果i是x的一个因子,那么x/i也是x的一个因子。

#include<iostream>
using namespace std;
bool IsPrime(int x)
{
	if(x<2)
		return false;
	for(int i=2;i<=x/i;i++)//i<=x/i而不用i*i<=x是为了防止数据溢出
	{
		if(x%i==0)
			return false;
	}
	return true;
}

2、分解质因数(试除法)O(logn)~O(sqrt(n))
思路:遍历所有的从2~x的数

#include<iostream>
using namespace std;
void devide(int x)
{
	for(int i=2;i<=x/i;i++)//枚举到sqrt(x)即可,因为一个数最多只有一个大于sqrt(x)的因子
	{
		if(x%i==0)
		{
			int s=0;
			while(x%i==0)
			{
				x/=i;//i是分解出的质因数
				s++;//每个质因数的指数
			}
			cout<<i<<' '<<s<<endl;
		}
	}
	if(x>1)
		cout<<x<<' '<<1<<endl;//最后一个大于sqrt(x)的质因子,有且只有一个
}

int main()
{
	int n;
	cin>>n;
	while(n--)
	{
		int x;
		cin>>x;
		devide(x);
		cout<<endl;
	}
	return 0;
}

二、素数(质数)筛选

1、素数筛选
(1)埃氏筛法O(nloglogn)
原理:在筛选质数的同时,把所有质数的倍数筛掉,因为质数的倍数一定是合数,所以不用去一个一个地筛掉每一个数的倍数。

#include<iostream>
using namespace std;
const int N=1e5;
int prime[N],cnt;//存储素数
bool st[N];//判断数是否被筛掉
void getPrime(int n)//筛选从2~x里的所有素数
{
	for(int i=2;i<=n;i++)
	{
		if(!st[i])
		{
			prime[cnt++]=i;
			for(int j=i+i;j<=n;j+=i)//筛去质数的倍数
				st[j]=true;
		}
	}
}

(2)线性筛
核心:1~n内的合数只会被其最小质因子筛掉。用每个数的最小质因子去筛掉它,保证每个数只会被筛一次。从最小质数开始筛的,每个数一定有一个最小的质因子,用每个数的最小质因子来筛掉它,就可以保证每个数只会被筛一次。

void getPrime(int n)
{
	for(int i=2;i<=n;i++)
	{
		if(!st[i])
			prime[cnt++]=i;
		for(int j=0;prime[j]<=n/i;j++)
		{
			st[i*prime[j]]=true;
			if(i%prime[j]==0)//prime[j]一定是i的最小质因子,也是i*prime[j]的最小质因子,这一步保证了每个数最终只会被筛一次
				break;
		}
	}
}
int main()
{
	int n;
	cin>>n;
	getPrime(n);
	cout<<cnt<<endl;
	return 0;
}

三、约数
1、求约数(试除法)

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> item;
vector<int> get_divisors(int x)
{
	vector<int> res;
	for(int i=1;i<=x/i;i++)
	{
		{
			if(x%i==0)
			{
				res.push_back(i);
				if(i!=x/i)//特判重复的
					res.push_back(x/i);
			}
		}
	}
	sort(res.begin(),res.end());
	return res;
}
int main()
{
	int n,x;
	scanf("%d",&n);
	while(n--)
	{
		item.clear();//记得清空
		scanf("%d",&x);
		item=get_divisors(x);
		for(auto items:item)
			printf("%d ",items);
		printf("\n");
	}
	return 0;
}

2、求约数个数
公式:(a1+1)*(a2+1)*...*(ai+1),ai是对应的质因子的指数。
对于任何一个数n,可分解为质因数n=P1^(a1)*P2^(a2)...Pk^(ak),而对于n的任何一个约数d又可表示成d=P1^(b1)*P2^(b2)...Pk^(bk),其中1<=bi<ai。所以每个bi有ai+1中取法(0~ai),所以n一共会有(a1+1)*(a2+1)*...*(ai+1)个约数。

#include<iostream>
#include<unordered_map>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int main()
{
	int n;
	unordered_map<int,int> primes;//让底数和指数一一映射
	cin>>n;
	while(n--)//分解质因子
	{
		long long x;
		cin>>x;
		for(int i=2;i<=x/i;i++)
		{
			if(x%i==0)
			{
				while(x%i==0)
				{
					x/=i;
					primes[i]++;//存放因数i的指数
				}
			}
		}
		if(x>1) primes[x]++;
	}
	long long res=1;
	for(auto prime:primes)
		res=res*(prime.second+1)%mod;
	cout<<res<<endl;
	return 0;
}

3、求n个数的乘积的约数之和
公式:(P1^0+P1^1+...+P1^ak)*(P2^0+P2^1+...+P2^ak)*...*(Pk^0+P1^1+...+Pk^ak),Pi是所有数分解质因数后的结果。
把这个式子拆开可以得到多个(P1^b1*P2^b2*...*Pk^bk)相加的多项式,每一项一定是n的一个约数。

#include<iostream>
#include<unordered_map>
using namespace std;
const int mod=1e9+7;
typedef long long ll;
int main()
{
	int n;
	unordered_map<int,int> primes;
	cin>>n;
	while(n--)
	{
		ll x;
		cin>>x;
		for(int i=2;i<=x/i;i++)
		{
			if(x%i==0)
			{
				while(x%i==0)
				{
					x/=i;
					primes[i]++;
				}
			}
		}
		if(x>1) primes[x]++;
	}
	ll res=1;
	for(auto prime:primes)
	{
		ll p=prime.first,a=prime.second;
		ll t=1;
		while(a--)
		{
			t=(t*p+1)%mod;//公式
		}
		res=res*t%mod;
	}
	cout<<res<<endl;
	return 0;
}

4、求最大公约数

#include<iostream>
using namespace std;
int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}

四、欧拉函数

1、873. 欧拉函数
φ(n)求1~n中与n互质的数的个数。
互质,即两个数的最大公约数只有1,gcd(a,b)=1。
对n分解质因数:n=p1^(a1)*p2^(a2)*...*pk^(ak)
公式:φ(n)=n*(1-1/p1)*(1-1/p2)*...*(1-1/pk)

#include<iostream>
using namespace std;
int phi(int x)//分解质因数+欧拉公式
{
	int res=x;
	for(int i=2;i<=x/i;i++)
	{
		if(x%i==0)
		{
			res=res/i*(i-1);//res=res*(1-i/i)因为1/i要是取整的,所以换一种形式写
			while(x%i==0)
				x/=i;
		}
	}
	if(x>1) res=res/x*(x-1);
	return res;
}
int main()
{
	int n;
	scanf("%d",&n);
	while(n--)
	{
		int x;
		scanf("%d",&x);
		printf("%d\n",phi(x));
	}
}

2、筛法求欧拉函数
求1~n中每个数的欧拉函数之和
原理:利用线性筛+欧拉函数的公式

#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int phi[N],prime[N],flag[N],cnt;
ll get_euler(int n)
{
	phi[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!flag[i])
		{
			prime[cnt++]=i;
			phi[i]=i-1;//i是质数时,与它互质的数除了它本身
		}
		for(int j=0;prime[j]<=n/i;j++)
		{
			flag[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];//prime[j]是i的最小质因子,所以i*prime[j]的质因子已经在φ(i)中计算过了,只需要改变N的值即可
				break;
			}
			//prime[j]是i*prime[j]的质因子,
			//所以phi[i*prime[j]]=i*prime[j]*(1-1/(Pk))*(1-1/prime[j]),而i*(1-1/(Pk))=φ(i),
			//故phi[i*prime[j]]=phi[i]*prime[j]*(1-1/prime[j])=phi[i](prime[j]-1)。
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
    ll res=0;
	for(int i=1;i<=n;i++)
		res+=phi[i];
	return res;
}
int main()
{
	int n;
	scanf("%d",&n);
	printf("%lld\n",get_euler(n));
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值