一、质数
质数,合数:都是针对大于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;
}