1.质数
1.质数的判定:
bool fun(int n){
for(int i=2;i<=n/i;i++)
if(n/i==0) return false;
return true;
}
枚举到n/i既可以优化速度也可以有效防止溢出。
2.分解质因数:
一个数n最多有一个质因数大于sqrt(n);
void fun(int n){
for(int i=2;i<=n/i;i++){//枚举2到sqrt(n);
if(n%i==0){//i是质数;
int cnt=0;
while(n%i==0){
n/=i;
cnt++;
}
cout<<i<<' '<<cnt<<endl;
}
}
if(n>1) cout<<n<<' '<<1<<endl;//(特殊情况);
}
这里出现的只会是素数,因为当把一个素数除干净的时候,就不会出现以这个素数为因数的合数作为约数。
3.筛质数
基本思路: 在2-n之中,i从2开始枚举,删除掉i的倍数,剩下的就是质数;
埃氏筛法:
int a[N];
bool st[N];
int cnt;
void fun(int n){
for(int i=2;i<=n;i++){
if(!st[i]){
a[cnt++]=i;
for(int j=i+i;j<=n;j+=i)
st[j]=true;
}
}
}
线性筛,核心:用最小质因数删去所有的合数,并且保证只删除一次;
int a[N];
bool st[N];
int fun(int n){
int cnt=0;
for(int i=2;i<=n;i++){
if(!st[i]) a[cnt++]=i;
for(int j=0;a[j]<=n/i;j++){
st[a[j]*i]=true;
if(i%a[j]==0) break;
}
}
return cnt;
}
求约数的个数;
先将n分解质因数,其中假如一个质因数为,p^a,每个质因数都可以都有0~a种选法,所以n的约数个数为每个质因数的次数+1累乘的结果;
//朴素写法
int fun(int n){
int res=1;
for(int i=2;i<=n/i;i++){
if(n%i==0){
int s=0;
while(n%i==0){
s++;
n/=i;
}
res=res*(s+1)%MOD;
}
}
if(n>1) res=res*2%MOD;
return res;
}
//哈希表写法;
int fun1(int n){
unordered_map<int,int>a;
int res=1;
for(int i=2;i<=n/i;i++){
while(n%i==0){
a[i]++;
n/=i;
}
}
if(n>1) a[n]++;
for(auto i:a) res=res*(i.second+1)%MOD;
return res;
}
2,求约数的和:
仍然是分解质因数,
约数之和就是分解出的每个约数(p^a),然后在进行质因数分解,
最后相乘即可;
约数之和: (p10+p11+…+p1c1)∗…∗(pk0+pk1+…+pkck)
int fun(int n){
unordered_map<int,int>s;
for(int i=2;i<=n/i;i++){//分解质因数;
while(n%i==0){
s[i]++;
n/=i;
}
}
if(n>1) s[n]++;
int ans=1;
for(auto i:s){
int t=1;
int p=s.first,a=s.second;
while(a--) t=t*p+1;//每个约数求和
ans=ans*t;//累乘;
}
return ans;
}
3.欧拉函数:
欧拉函数就是得到1~n之中和n互质的数的个数;
其中p为质因数;
公式:ϕ(N) = N∗(p1−1/p1)∗(p2−1/p2)∗…∗(pm−1/pm)
int fun(int n){
int res=n;
for(int i=2;i<=n/i;i++){
if(n%i==0){
res=res/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n>1) res=res/n*(n-1);
return res;
}
3.线性筛求欧拉函数;
套用线性筛的模板即可;
int a[N],cnt;
bool st[N];
int phi[N];
void fun(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!st[i]) a[cnt++]=i,phi[i]=i-1;
for(int j=0;a[j]<=n/i;j++){
st[a[j]*i] =true;
if(i%a[j]==0){
phi[a[j]*i]=phi[i]*a[j];
break;
}
phi[a[j]*i]=phi[i]*(a[j]-1);
}
}
}
详解:当i是质数的时候,他的互质数的个数就是1~i-1,phi[i]=i-1;
当i%a[j]=0时,说明i的最小质因数是a[j],
同时i*a[j]的最小质因数也是a[j],
*//a[j]*i的质因子其实是和i的质因子时相同的,只是多乘了一个a[j],不会出现
//其他的质因子;*
计算i*a[j]的欧拉函数时,因为在计算i的欧拉函数时已经计算过一次phi[i],
所以i*a[j]的欧拉函数就是a[j]*phi[i];
当i%a[j]!=0时,说明a[j]比i之前的数的最小质因数,
所以计算phi[a[j]*i]=phi[i]*a[j]*(1-1/a[j]),
化简可得:phi[a[j]*i]=phi[i]*(a[j]-1);
4.快速幂;
求a^b%p的结果;
可以把b转化成二进制,当算b的二进制的某一位的时候,如果是1,就相乘,a每次都要跟自己进行平方运算;
int fun(int a,int b,int p){
int res=1;
while(b){
if(b&1) res=(ll) res*a%p;
a=(ll)a*a%p;
b>>=1;
}
return res;
}
附加题:快速幂
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<cmath>
using namespace std;
typedef long long ll;
int fun(int a,int b,int p){
int res=1;
while(b){
if(b&1) res=(ll)res*a%p;
a=(ll)a*a%p;
b>>=1;
}
return res;
}
int main(){
int n;
cin>>n;
unordered_map<int,int>a;
for(int i=2;i<=n;i++){
int k=i;
for(int j=2;j<=k/j;j++){
while(k%j==0){
a[j]++;
k/=j;
}
}
if(k>1) a[k]++;
}
int res=1;
a[2]-=a[5];
for(auto i:a){
int p=i.first,k=i.second;
if(p==5) continue;
int o=fun(p,k,10);
res=res*o%10;
}
cout<<res<<endl;
return 0;
}
有关数论的一些知识:
同余:假如a mod m=b mod m,称为a与b 对m取模同余;
费马定理:假如p是质数,ap对p取模同余a;
例子:乘法逆元:
若整数b,m互质,并且对于任意的整数 a,如果满足b|a,则存在一个整数x,使得a/b≡a∗x(mod m),则称x为b的模m乘法逆元,记为b−1(mod m)。
b存在乘法逆元的充要条件是b与模数m互质。当模数m为质数时,bm−2即为b的乘法逆元。
这里给a/b≡a∗x(mod m)同时成b,再将a除过去就可以得到,bx对m取模同余1,有根据费马定理bp, , bx≡bm-1(mod m),x=bm-2;
欧拉定理:若a与n互质,则aphi(n)mod n=1;
欧几里得算法:
一般用于求最大公约数,直接__gcd也可hh。
推导过程:
一般情况下假如一个整数d可以被x整除,也可以被y整除,那么它可以被ax+by整除;ab为系数;
gcd(a,b)=gcd(b,a mod b)
因为a mod b=a-floor(a/b)*b,所以a mod b也可以被d整除;
可以推出:gcd(a,b)=gcd(b,a mod b);
int gcd(int a,int b){
return b? gcd(b,a%b):a;
}
扩展欧几里得算法:
找到一对系数xy满足,ax+by=gcd(a,b);
过程:
令n=gcd(a,b);所以gcd(b,a%b)=n;即by+a%bx=n;即
by+(a-(floor)a/bb)x=n;
合并同类项得:ax+b(y-a/bx)=n;
所以x没变,y变成(y-a/bx);
int gcd(int a,int b,int &x,int &y){
if(b==0){
x=1,y=1;
return a;
}
int d=gcd(b,a%b,y,x);
y=y-a/b*x;
return d;
}
应用:线性同余方程;
给定n组数据ai,bi,mi,对于每组数求出一个xi,使其满足ai∗xi≡bi(mod mi),如果无解则输出impossible。
链接:线性同余方程
题解:方程可以转化为:ax=my+b;即ax-my=b;
其中b一定时gcd(a,m)的倍数;
最后找到得出x,再给x乘b/d就好;
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int gcd(int a,int b,int &x,int &y){
if(b==0){
x=1,y=1;
return a;
}
int d=gcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
int main(){
int t;
cin>>t;
int a,b,m;
while(t--){
scanf("%d%d%d",&a,&b,&m);
int x,y;
int d=gcd(a,m,x,y);
if(b%d) puts("impossible");
else x=(ll)x*b/d%m,printf("%d\n",x);
}
return 0;
}