文章目录
因数
试除法分解质因数
void divide(int x)
{
for (int i = 2; i <= x / i; i ++ )
if (x % i == 0)
{
int s = 0;
while (x % i == 0) x /= i, s ++ ;
//杀掉这个书中所有包含这个质因数的全部数s代表的是这个质因数包含的个数,i是指这个质因数
cout << i << ' ' << s << endl;
}
if (x > 1) cout << x << ' ' << 1 << endl;
//最后的是代表的是如果最后x>1的话,那么这个x就一定是素数,那么一定还要输出最后的那个素数作为其质因数。
cout << endl;
}
//解释附在代码片中辣。
素数(质数)
在自然数集中,小于n的质数大概有n/(ln(n))个
1.试除法判定素数
所谓试除法判定就是我们遇到一个素数就去判断小于等于sqrt(这个数)的所有数是否可以整除这个数,如果均不能那么就是素数,如果可以的话那么就break出去说明这个不是素数。
bool is_prime(int x)
{
if (x < 2) return false;
for (int i = 2; i <= x / i; i ++ )
//这里是一个小小的优化,省去了sqrt这一步
//到那时做到了和sqrt相同的效果
if (x % i == 0)
return false;
return true;
}
2.筛法求素数
朴素筛法求素数
朴素筛法就是将我们求的的素数的所有倍数全部筛出去,那么我们留下的就都是素数,因为我们没有被筛掉的数说明(2–>k-1)均不是它的因子,那必然是素数呵呵呵,没啥好说的…
int primes[N], cnt; // primes[]存储所有素数
bool st[N]; // st[x]存储x是否被筛掉
void get_primes(int n)
{
for (int i = 2; i <= n; i ++ )
{
if (st[i]) continue;
primes[cnt ++ ] = i;
for (int j = i + i; j <= n; j += i)
st[j] = true;
}
}
线性筛法求素数
int primes[N], cnt; // primes[]存储所有素数
bool st[N]; // st[x]存储x是否被筛掉
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;
//因为针对于所有的以2为最小质因数的合数一定可以被筛,如果i能被2整除,那么接下来的所有的数都能被2整除,那么在i的接下来的过程中的遍历的时候,都能将含有2的合数删去,如果i不能被整除,那么这个数中最小质因子就是大于primes[j]的数,那么我们接下来要去找,这个数的最小质因子就好.
}
}
}
解释附在代码片中。
约数
约数和因数的本质区别在于:
例如:1、2、4、8、16都能整除16,因此,1、2、4、8、16也都是16的约数。而当一个数被分解成两个或几个数相乘时,因数的个数就受到了限定。
又如:2×8=16。只能说2和8是16的因数,而不能说1、2、4、8、16都是的因数,因为1×2×4×8×16的结果,并不等于16.
试除法求所有的约数
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;
}
性质
针对于所有的约数都具备如下性质:
如果一个数P可以写成如下形式:
P=b1a1 ·b2a2 ········bkak
其中b123……k一定是所有的质因数
那么所有约数的个数便是(计数原理)
N=(a1+1)(a2+1)········(ak+1)
所有约数的个数便是
N=(b10 +b11 +b12 +……+b1a1)·(b20 +b21 +b22 +……+b2a2) ·(b30 +b31 +b32 +……+b3a3) ············(bk0 +bk1 +bk2 +……+bkak)
欧几里得算法(辗转相除法)
模板
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
这里需要用到整除原理:
如果d|b而且d|a那么d|(xa+yb),x和y分别是任意正整数那么(a,b)=(b,a mod b)
因为a mod b=a-[a/b]b;
那么此时x=1,y=-[a/b]
那么就一定可以整除a mod b,因此得证。
乘法逆元
如果一个线性同余方程
则 x 称为 a mod b 的逆元,记作a-1。
求乘法逆元在拓展欧几里得算法和快速幂算法中提到,因此本节只是介绍概念。
裴蜀定理
设 a,b 是不全为零的整数,则存在整数x,y, 使得
欧拉降幂
欧拉函数
欧拉函数φ(n)就是1-n中与n互质的数字的数目,其通式为:
(其中p1, p2……pn为x的所有质因数,x是不为0的整数),而且定义φ(1)=1。
欧拉定理
若n,a为正整数,且n,a互质,则:
一个小推论若正整数a与m 互质,则
费马小定理
如果p是一个质数,而整数a不是p的倍数,则
拓展欧拉定理
同时若
本式子恒成立
至此,便有了欧拉降幂的模板题
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define mode(a,b) a<b?a:a%b+b//欧拉降幂的精髓之处
using namespace std;
typedef long long ll;
ll mp[2102010];
ll ola(ll n){
if(mp[n]) return mp[n];
ll x=n;
ll s=n;
for(ll i=2;i<=n/i;i++){
if(n%i==0){
s-=s/i;
while(n%i==0) n/=i;
}
}
if(n>1) s-=s/n;return mp[x]=s;
}//求欧拉函数
ll qmi(ll m, ll k, ll p)
{
ll res = 1 % p, t = m;
while (k)
{
if (k&1) res = mode(res * t , p);
t = mode(t * t , p);
k >>= 1;
}
return res;
}
ll solve(ll a,ll b,ll mod){
if(mod==1||b==1) return mode(a,mod);
else return qmi(a,solve(a,b-1,ola(mod)),mod);
}
int main(){
char a[2102020],b[2102002];
scanf("%s",a);
scanf("%s",b);
ll len1=strlen(a);
ll len2=strlen(b);
ll aa=a[len1-1]-'0';
if(len1>1)aa=(a[len1-2]-'0')*10+aa;
if(len1>2)aa=(a[len1-3]-'0')*100+aa;
ll bb=b[len2-1]-'0';
if(len2>1)bb=(b[len2-2]-'0')*10+bb;
if(len2>2)bb=(b[len2-3]-'0')*100+bb;
if(aa==0){
printf("0");
}else{
cout<<solve(aa,bb,10)%10;
}
return 0;
}
快速幂
快速幂本幂
求 m^k mod p,时间复杂度 O(logk)。
int qmi(int m, int k, int p)
{
int res = 1 % p, t = m;
while (k)
{
if (k&1) res = res * t % p;
t = t * t % p;
k >>= 1;
}
return res;
}