快速幂 :
ll calc(ll x,ll y){
ll ans = 1;
while(y){
if(y & 1) ans = (ans * x) % k;
x = (x * x) % k;
y >>= 1;
}
return ans % k;
}
时间复杂度是logn;
运用二进制拆分;
快速乘:
应用于大整数相乘时,由于相乘后的结果会爆掉long long,所以化乘法为加法,然后取模。
a * b的实质是b个a相加,可以参考快速幂的思想运算。
a * b = 2 * (a * b / 2); b为偶数
a * b = 2 * (a * (b - 1) / 2 + a) ,b为奇数.
将b二进制拆分,然后进行加法即可。
ll ksc(ll a,ll b){
ll ans = 0;
while(b){
if(b & 1){
ans = (ans + a) % mod;
b --;
}
b >>= 1;
a = (a * 2) % mod;
}
return ans;
}
埃氏筛:
非常朴素的筛法,复杂度是O(nlogn)的。
欧拉筛:
void euler(){
not_p[0] = not_p[1] = 1;
for(int i = 2;i <= n;i ++){
if(!not_p[i]) p[++ tot] = i;
for(int j = 1;j <= tot && p[j] * i <= n;j ++){
not_p[i * p[j]] = 1;
if(i % p[j] == 0) break;
}
}
return;
}
关键在于跳出循环的条件的理解,i % p[j] == 0,这句话决定了所有的数都只能被他最小的质因数筛去。
p[i] 为 n的最小质因数;
考虑 n = m * p[i] ,n = m1 * p[i + k],则p[i] < p[i + k];
n 只会被p[i] 筛去,因为p[i] != p[i + k];
m = p[i + k] * m1 / p[i] ,即p[i] | m1;
则n = p[i] * p[i + k] * t ,其中 t = m1 / p[i];
则n仍会被p[i]筛去;
而对于i * p[i + k] 这些数则在循环到后面时被他的最小质因数筛去;
因为,每个数只会被他最小的质因数筛去,所以复杂度为O(n);
欧几里得:
int gcd(int a,int b){
return !b ? a : gcd(b,a % b);
}
gcd(a,b) = gcd(b,a % b)
证明:
不妨设 a > b
假设d为gcd (a,b)的一个公约数,则d|a d|b
设 r = a % b
r= a - k * b
r/d = a/d - (k * b) /d = m
m为整数
所以d|r
因此两者公约数相等,最大公约数也相等
当b为0是说明在递归的上一层中,a = k * b,则显然b为最大公约数。
裴蜀定理:
若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。即d不是gcd(a,b)的倍数时,ax+by=d无。
推论:gcd(a,b)=1时,ax+by=1才有解(有逆元的充要条件)
扩展欧几里得:
int exgcd(int a,int b,int &x,int &y){
if(b == 0){
x = 1,y = 0;
return a;
}
int ans = exgcd(b,a % b,y,x);
y -= a / b * x;
return ans;
}
求解ax + by = n的二元一次方程的解.
a,x,b,y是整数,则n是gcd(a,b)的倍数;
a / gcd * x + b / gcd * y = n / gcd;
显然左边是整数,则右边必然也是整数,否则无整数解;
先考虑求解ax1 + by1 = gcd(a,b)的解;
ax1 + by1 = gcd(a,b) = gcd(b,a % b) = bx2 + (a % b)y2;
a % b = a - a / b * b;
化简得 :
x1 = y2;
y2 = x2 - a / b * y2;
则最后的答案是x = x1 * n / gcd(a,b) , y = y1 * n / gcd(a,b);
应用 : 求二元一次方程,一元同余方程的多组解;
逆元:
ax mod m = 1 mod m,则称x是a mod m时的逆元;
求解逆元的方法:
1、线性递推求逆元。
2、exgcd求解。
3、费马小定理。
逆元的应用:
1、大整数除法取模
由于除法在mod的时候不支持结合律,所以在大数相除的时候,考虑到(a / b)mod m时会损失大量的精度,由此,可以用逆元将除法转化为乘法。
设x为b mod m时的逆元,(a \ b)mod m = (a \ b) mod m * ( (b * x) mod m ) = (a * x) mod m;
2、求解二元一次方程的解;
设A 是 a mod m时的逆元,对于二元一次方程有:
ax + my = b;
转化为求
a0 * x + m0 * y = b / gcd(a,b);
a0 = a / gcd(a,b);
m0 = m / gcd(a,b);
即求 a0 * x0 + m0 * y0 = 1,且满足a0 和 m0互质;
等价于 a0 * x0 mod m0 = 1 mod m0;
x0 = A
x = x0 * b
y = y0 * b
费马小定理:
a ^ (p - 1) mod p = 1 mod p; 其中a 和 p互质;
证明:
1.1引理1 : a mod m = b mod m;
设c 与 m互质 ,则满足 a * c mod m = b * c mod m;
1.1证明 : 显然
从模n的每个剩余类中各取一个数,得到一个由n个数组成的集合,叫做模n的一个完全剩余系。——百度百科
1.2引理:若 0,1,2,3, …… m - 1 是 a mod m的完全剩余系;
则 0 * c, 1 * c, 2 * c,3 * c …… (m - 1)* c也是a mod m的完全剩余系,c 与 m 互质。
1.2证明:假设序列2中存在两个数i, j ,使得 i * c mod m = j * c mod m。
由引理1.1知 i = j,不符,假设不成立,所以序列2中的数在mod m后是互不相同的m个数,由于mod m后的值不可能大于m,所以序列2是 a mod m的完全剩余系,但注意并不与等式左边一一对应;
因此
序列1 : 1,2,3,4……m - 1;
序列2 :1 * c,2 * c,3 * c,4 * c …… (m - 1)* c
满足 1 * 2 * 3 * 4 …… * (m - 1) mod m = c * 2 * c * 3 * c * 4 …… * (m - 1)* c mod m;
即 (m - 1)! mod m = (m - 1)! * c ^ (m - 1) mod m;
即 a ^ (p - 1) mod p = 1 mod p ,p与m互质;
显然,a ^ (p - 2) 是a mod p时的逆元;
求解二元一次方程的解的两种方法:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a,b,c;
int gcd(int a,int b){
return !b ? a : gcd(b,a % b);
}
void exgcd(int a,int b,int &x,int &y){
if(!b){
x = 1,y = 0;
return;
}
exgcd(b,a % b,y,x);
y -= (a / b) * x;
return;
}
int ksm(int a,int x){
int tmp = 1;
while(x){
if(x & 1) tmp = (tmp * a) % b;
a = (a * a) % b,x >>= 1;
}
return tmp % b;
}
void solve(){
int a0,b0,x,y,ni,g;
cin >> a >> b >> c;
g = gcd(a,b),a0 = a / g,b0 = b / g,c /= g;
if(b0 - 2 > 0){
ni = ksm(a0,b0 - 2);
cout << ni * c<< " " << (c - a0 * ni * c) / b0 << endl;
}
exgcd(a,b,x,y);
cout << x * c << " " << y * c << endl;
return;
}
int main(){
solve();
return 0;
}
欧拉定理 和 欧拉函数:
Miller-Rabin素数测试:
约数定理与约数和定理:
威尔逊定理:
斯特林公式:
勒让德定理:
欧拉降幂: