一.质数
筛质数:
埃氏筛法:复杂度O(nlogn);
基于的定理是:唯一分解定理:每个数可以分解为唯一的一组质数相乘;
对于每个质数属于[1,n]的倍数部分都先筛出,因为质数的倍数必然不是质数。
可以想一下:假设一个数x不是质数,那么他必有一个质因子y小于x,那么x必将会在循环到y的时候筛除。
优化:我们可以知道我们一定是重复筛除了一些数,事实上我们可以这么做,当我们循环到i时,筛除小于i的质数的i倍的数,但是这样好像没有优化,不妨我们考虑一下,一个数可以被分解为多个质数相乘,那么当i为某个质数z的时候倍数时,那么任何质数的i倍必然会被z筛除。这时候我们可以break,让后面z来筛出。这就是各司其职。
补充:假设某个数m,有质数x,y(x<y),假设m被y所筛出,那么m/y<m/x,那么说m/y必然被x整除,那也就是说从小到大而言,当i=m/y时,必然不会轮到y。所以m不被y所筛出。
题目链接:活动 - AcWing
代码实现:
#include<iostream>
using namespace std;
const int N=1000100;
int primes[N],cnt;
bool st[N];
//效筛法率较低的
/*void get_primes(int n)
{
for(int i=2; i<=n; i++)
{
if(st[i]) continue;
primes[cnt++]=i;
for(int j=2; i*j<=n; j++ )
{
st[j*i]=true;
}
}
cout<<cnt<<endl;
}*/
//稍微快一点的筛法(线性筛法)
void get_primes(int n)
{
for(int i=2; i<=n; i++)
{
if(!st[i]) primes[cnt++]=i;
//一定每次都要循环,因为要筛掉质数的i倍数
for(int j=0; primes[j]<=n/i; j++)
{
st[primes[j]*i]=true;//我们将质数的i倍筛出,由于i的质数倍一定比i大,因此,后面的i一定不是质数
if(i%primes[j]==0) break;//如果i不是质数,他一定在前面或者后面就被筛了,因为任何质数的i倍都是,某一个质数的x倍(这个一定会被筛掉)
}
}
cout<<cnt<<endl;
}
int main()
{
int n;
cin>>n;
get_primes(n);
}
求某个数的质数:采用试除法
时间复杂度O()
题目链接:找不到页面 - AcWing
直接放代码:
#include<iostream>
using namespace std;
void is_prime(int n)
{
for(int i=2; i<=n/i; i++)
{
if(n%i==0)//i一定是质因数
{
int cnt=0;
while(n%i==0)
{
cnt++;;
n/=i;
}
cout<<i<<' '<<cnt<<endl;
}
}
if(n!=1) cout<<n<<' '<<1<<endl;
cout<<endl;
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
int x;
scanf("%d",&x);
is_prime(x);
}
}
二.约数
下面三个定理记住即可
约数个数:把一个数N 写成:N = (p1^x1^)(p2^x2)(p3^x3)…(pk^xk),其中pi为质数。则N的约数个数为:(x1+1)(x2+1)(x3+1)…(xk+1)
约数和:(+p12+…+p1x1)∗…∗(pk0+pk1+…+pkxk)
最大公约数:辗转相除法:gcd(a,b)=gcd(b,a%b),当某一个为0时停止;
三.欧拉函数
欧拉函数定义:对于一个数n,欧拉函数φ(n)为小于等于n的数中与n互质的个数。
先给公式再证明:
p为n的分解质数证明如下:
首先对于每个质数,我们可以筛除n/pi的个数,即n-n/pi,但是这样会多删除一部分,比如n/(pi*pj)的部分,所以有需要加上一部分。如此反复循环。然后整合我们可以得到上述的公式(有点像概率论里面的一个公式)
欧拉函数的递推应用:使用到的是欧拉函数是积性函数
给定一个正整数 n,求 1∼n 中每个数的欧拉函数之和。
输入格式
共一行,包含一个整数 n。
输出格式
共一行,包含一个整数,表示 1∼n 中每个数的欧拉函数之和。
我们可以做那么一个推导:从小到大循环1-n,对于每个数,如果这个数为质数,那么他的欧拉函数为i-1,同时把这个数加入primes。接下来我们按照优化后的筛质数步骤来进行,遍历每个已有的质数,对于每个质数的i的倍数,我们可以知道优化后的倍数只执行一次(只是被最小质因子筛),那么对于i*primes来说,i%primes==0,那么意味着primes出现过,于是ans[i*primes]=primes*ans[i] (实际上是对i*primes的补充);如果i与primes互质,那么可以直接用积性函数进行计算。那么如何保证不重不漏的计算了所有的贡献呢?注意我们代码的逻辑,每个数只被最小质因子筛一次。那么也就是说对于某个数m=p1*p1*p2*p3(p1出现两次);那么m的欧拉函数应该为p1*p1*p2*p3*(1-1/p1)*(1-1/p2)*(1-1/p3),转化一下就变为p1*p1(1-1/p1)*p2*(1-1/p2)*p3*(1-1/p3),实际上就是m=p1*(m/p1的欧拉函数);所以可以知道算法一定是不重不漏的计算了所有的值
代码:
#include<iostream>
#include<unordered_map>
#include<vector>
using namespace std;
const int mod=1e9+7;
typedef long long LL;
int main(){
int n;
cin>>n;
//int res=1;
vector<LL>ans(n+1,0);
ans[1]=1;
vector<int>primes;
vector<bool>st(n+1,false);
for(int i=2; i<=n; i++){
if(!st[i]){
primes.push_back(i);
ans[i]=i-1;
}
for(auto v:primes){
if(v*i>n)break;
st[v*i]=true;
if(i%v==0){
ans[i*v]=v*ans[i];
break;
}
ans[i*v]=(v-1)*ans[i];
}
}
LL res=0;
for(int i=1; i<=n; i++)res+=ans[i];
cout<<res<<endl;
}
四.快速幂
快速幂:实际上是二进制拆分,将指数拆分为二进制数,对于该位上为1的数,进行相乘;
代码:注意开LL
#include<iostream>
#include<unordered_map>
#include<vector>
using namespace std;
const int mod=1e9+7;
typedef long long LL;
LL qmi(LL a, LL b, int c){
LL res=1;
while(b){
if(b&1){
//cout<<b<<' '<<a<<endl;
res*=a;
res%=c;
}
a=a*a%c;
b>>=1;
//cout<<b<<endl;
}
return res;
}
int main(){
int n;
cin>>n;
while(n--){
int a,b,p;
cin>>a>>b>>p;
cout<<qmi(a,b,p)<<endl;
}
}
快速幂求逆元:
题目信息:
前置知识:
同余符号:如果a%p==b%p,那么a和b%p同余;
b|a,即a被b整除
证明:
如果b|a 同时(a/b)%m=(a*x)%m
由于取模的运算特性可得:(1/b)%m=x%m
得 1%m=b*x%m
又因为 1%m=b^(m-1)
所以x=b^(m-2)%m;
代码如下:
#include<iostream>
using namespace std;
const int N=1e5+10;
typedef long long LL;
LL qmi(LL a, LL b, LL p){
LL res=1;
while(b){
if(b&1){
res=res*a%p;
}
a=a*a%p;
b>>=1;
}
return res;
}
int gcd(int a, int b){
return b?gcd(b,a%b):a;
}
int main(){
int n;
cin>>n;
for(int i=0; i<n; i++){
int a,p;
cin>>a>>p;
if(gcd(a,p)==1){
cout<<qmi(a,p-2,p)<<endl;
}else cout<<"impossible"<<endl;
}
}