目录
一、质数
(1)试除法判断质数
试除法,即对一个数n,从2到,判断能否整除n。若有一个数能,则n为合数,若都不能,为质数。注意为了不要超出int表达范围,写成
//这里填你的代码^^
#include <iostream>
#include <algorithm>
using namespace std;
bool is_prime(int x)
{
if (x < 2) return false;
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
return false;
return true;
}
int main()
{
int n;
cin >> n;
while (n -- )
{
int x;
cin >> x;
if (is_prime(x)) puts("Yes");
else puts("No");
}
return 0;
}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3740017/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(2)分解质因数
将一个数N分解为如下形式:
即可令从2到
,判断能否整除N,若能,则不断让N除以i,求出
的次数。
注意:(1)若n能整除i,则i一定是一个质数。因为若是i合数,则必能拆成两个因子,这些因子在先前已被遍历到,即此时N不能被这些因子整除,自然也不能被i整除。
(2)可能会有一个大于的质因子,在分解完之前的质因子后,N的值就是这个剩下的质因子。因此循环遍历到
即可,但最后要加上判断。
//这里填你的代码^^
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
void divide(int n)
{
for(int i=2;i<=n/i;i++)//大于根号n的质数最多有一个,所以这里枚举到根号n
if(n%i==0)//i必定是个质数
{
int s=0;
while(n%i==0)
{
n/=i;
s++;
}
printf("%d %d\n",i,s);
}
if(n>1) printf("%d %d\n",n,1);//此时n就是大于根号n的那个质数
puts("");
}
int main()
{
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
divide(x);
}
return 0;
}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3740285/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(3)筛法求质数
埃式筛法
即每遍历到一个没有被筛去的数,就把它的倍数筛去。同时可以注意到这个没有被筛去的数一定是个质数,如此操作遍历整个集合,剩下的数就是质数(每一个合数已经被它的某一个质因子筛掉了)。
//这里填你的代码^^
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N =1e6;
int primes[N],cnt;
bool st[N];
void get_primes(int n)
{
for(int i=2;i<=n;i++)
if(!st[i])
{
primes[cnt++]=i;
for(int j=i+i;j<=n;j+=i)
st[j]=true;
}
}
int main()
{
int n;
cin>>n;
get_primes(n);
cout<<cnt<<endl;
return 0;
}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3743520/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
线筛法(时间复杂度线性)
核心:每个数(primes[j]*i)只用它的最小质因子来筛去。每个数只有一个最小质因子,因此每个数只需要被筛一次,因此是线性的。
分析:(1)若循环内i%primes[j]==0,则primes[j]是i的最小质因子,同时也是primes[j]*i的最小质因子。
(2)若循环内i%primes[j]!=0,则primes[j]一定小于i的所有质因子,则primes[j]*j的最小质因子也是primes[j]。
(3)每一个合数都能被删去:对于任意一个合数x,都能找到其最小质因子primes[j],当循环遍历到primes[j]时,x就会被primes[j]*i筛去。
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N =1e6;
int primes[N],cnt;
bool st[N];
void get_primes(int n)
{
for(int i=2;i<=n;i++)
{
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++)
{
st[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
int main()
{
int n;
cin>>n;
get_primes(n);
cout<<cnt<<endl;
return 0;
}
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3743520/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
二、约数(求约数、和、积、最大公约数)
(1)试除法求约数
求得一个数x的一个约数i,则x/i也是x的一个约数。遍历到即可得x的所有约数(注意若约数i恰好是根号x,则不需再将x/i也加入约数集合中)
//这里填你的代码^^
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
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;
cin>>n;
while(n--)
{
int x;
cin>>x;
auto res=get_divisors(x);
for(auto x:res) cout<<x<<" ";
cout<<endl;
}
return 0;
}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3744545/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(2)约数个数
首先介绍一个数N的约数个数的公式:
对于一个数N,一定能够将N写成:
同样的我们可以设d为N的一个约数,可以把d写成
,
其中易知,对于d中因子的次方可取
,即对于每一个因子次数,都用
种取法。则可知d的取值有
种,即为约数个数。
其中,要求因子拆分至不可拆分,即要求因子为质因子。
//这里填你的代码^^
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
int mod=1e9+7;
int main()
{
int n;
cin>>n;
unordered_map<int,int> prime;
while(n--)
{
int x;
cin>>x;
for(int i=2;i<=x/i;i++)
if(x%i==0)
{
while(x%i==0)
{
x/=i;
prime[i]++;
}
}
if(x>1) prime[x]++;
}
long long res=1;
for(auto p:prime) res=res*(p.second+1)%(mod);
cout<<res<<endl;
return 0;
}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3744643/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(3)约数之和
同理拆分每一个约数d为:
则将每一个d累加,分解因式得:
//这里填你的代码^^
#include<iostream>
#include<algorithm>
#include<unordered_map>
using namespace std;
int mod=1e9+7;
typedef long long LL;
int main()
{
unordered_map<int,int> primes;
int n;
cin>>n;
while(n--)
{
int x;
cin>>x;
for(int i=2;i<=x/i;i++)
while(x%i==0)
{
x/=i;
primes[i]++;
}
if(x>1) primes[x]++;
}
long long res=1;
for(auto p:primes)
{
LL a=p.first,b=p.second;
LL t=1;
while(b--) t=(t*a+1)%mod;
res=res*t%mod;
}
cout<<res<<endl;
return 0;
}
//注意代码要放在两组三个点之间,才可以正确显示代码高亮哦~
作者:yankai
链接:https://www.acwing.com/activity/content/code/content/3745052/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(4)最大公约数(欧几里得算法)
若d能整除a,d能整出b,则d能整除a和b的线性组合。因此(a,b)=(b,a mod b),左边的约数是右边的约数,右边的约数也是左边的约数。如此循环操作直至b==0,(a,0)的最大公约数为a,则a为原原式最大公约数。
#include<iostream>
using namespace std;
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
int main()
{
int n;
cin>>n;
while(n--)
{
int a,b;
cin>>a>>b;
cout<<gcd(a,b)<<endl;
}
return 0;
}
三、欧拉函数
(1)定义及常规求法
定义:1~N中与N互质的数的个数称为欧拉函数,记为。若在算数基本定理中,
,
则:
或写成:
证明:容斥原理。
求欧拉函数代码如下:
#include<iostream>
using namespace std;
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);
cout<<res<<endl;
}
return 0;
}
(2)线筛法求欧拉函数
分类讨论:
(1)若N为质数,则显然N质因子只有N,。
(2)若N不为质数:在线筛法中,需要找出被筛去的数的欧拉函数,即primes[j]*i。其中有两种情况。
1.i%primes[j]==0,则primes[j]*i的质因子与i完全相同,所以相比之下只有“N”变了,因此
2.i%primes[j]!=0,则primes[j]*i的质因子在i的基础上多了一个primes[j],因此
线筛法求欧拉函数代码如下:
#include<iostream>
#include<algorithm>
using namespace std;
const int N =1000010;
int primes[N],cnt;
int phi[N];
bool st[N];
typedef long long LL;
void get_euler(int n)
{
phi[1]=1;
for(int i=2;i<=n;i++)
{
if(!st[i])
{
primes[cnt++]=i;
phi[i]=i-1;//i是质数,显然从1~i-1都是与i互质的
}
for(int j=0;primes[j]<=n/i;j++)
{
st[primes[j]*i]=true;
if(i%primes[j]==0)
{
phi[i*primes[j]]=phi[i]*primes[j];
break;
}
phi[primes[j]*i]=phi[i]*(primes[j]-1);
}
}
}
int main()
{
int n;
cin>>n;
LL res=0;
get_euler(n);
for(int i=1;i<=n;i++)
{
res+=phi[i];
}
cout<<res;
return 0;
}