1.素数
欧拉筛法(线性筛法)
用每个数(可以为合数)与已经筛出的质数筛掉别的合数。
欧拉筛法做到线性的秘诀在于保证每个合数只被它最小的素因子筛一次。
若p|i,那么让p大的素数来筛出iq时,
iq=q∗(ip)∗p
,所以不是最小素因子,可以在访问到p的时候跳出循环了。
const int N = 100000 + 10;
int is_prime[N], prime[N], tot;//is_prime[p] == 0表示p为素数,tot为素数的个数
void find_prime(){
is_prime[1] = 0;
for(int i = 2; i < N; i++){
if(!is_prime[i])
prime[++tot] = i;
for(int j = 1; j <= tot && i * prime[j] < N; j++){
is_prime[i*prime[j]] = 1;
if(i % prime[j] == 0) break;//以此做到线性
}
}
}
2.同余
扩展欧几里得算法(ex-gcd)
给定a,b,c,求满足
ax+by=c
的一组解
显然若有解
gcd(a,b)=c
,然后可以转化为求
ax+by=1
类似于gcd一样的迭代,考虑若
bx′+(a%b)y′=1
的解,因为
a%b=a−(a/b)∗b
所以有解
x=y′
,
y=x′−(a/b)y′
。
int x, y;
int exgcd(int a, int b){
if(b == 0){
x = 1, y = 0;
return a;
}
int tmp1 = exgcd(b, a%b);
int tmp = x;
x = y;
y = tmp - (a/b)*y;
return tmp1;
}
//返回值顺便求出了a,b的最大公约数g,此时得到一组解(x, y),则(x + kb/g, y - ka/g)为其他解
//求整数x和y,使得ax+by=d,且|x|+|y|最小。其中d=gcd(a,b)
void exgcd(LL a, LL b, LL &d, LL &x, LL &y){
if(b == 0) d = a, x = 1, y = 0;
else exgcd(b, a % b, d, y, x), y -= x * (a / b);
}
同余和逆元
对于整数a,b,m,如果
a%m=b%m
,称a和b在模m意义下同余,记作
a≡b(modm)
对于整数x,y,m,如果
xy%m=1
,称y是x模m的逆元。
求逆元
解线性同余方程
就是解 ax≡b(modm) ,逆元为b=1的特殊情况
方程可以转化为 ax+my=b ,应用ex-gcd可求。费马小定理
对于一个素数p,及一个不是它的倍数的整数x,有
xp−1≡1(modp)
变形可得 xp−2≡1/x(modp)
所以这种情况下 xp−2 即为x的逆元
应用快速幂可求
适用范围:p为素数,x不为p的倍数欧拉定理
若 gcd(a,b)=1
aφ(b)≡1(modb)
其中 φ(b) 表示1到b里和b互质的数的个数。
同理 aφ(b)−1 为a的逆元
应用快速幂及欧拉函数可求奇技淫巧
求n以内阶乘逆元
n((n!)−1)=(n−1)!−1
应用递推可以线性求O(n) 预处理 1…n(n<p) 的逆元
先假设我们已经求出来了 1…i−1 的逆元 f() 。
设 p=x×i+y(0<y<i)
那么 x×i+y=0(modp)
⇒−x×i=y(modp)
同时除以 iy
⇒−x×f(y)=f(i)(modp)
⇒f(i)=−(p/i)∗f(p%i)(modp)
代换负数
⇒f(i)=(p−p/i)∗f(p%i)(modp)
p为质数适用。
3.组合计数
组合数的求法,模p意义下
组合公式+逆元
n!逆元可以线性递推得到,在n,m不大的情况下可以预处理出所有阶乘和逆元,直接求出组合数。
Lucas定理
A、B是非负整数,p是质数。AB写成p进制:A=a[n]a[n−1]...a[0]B=b[n]b[n−1]...b[0]
则组合数 C(A,B) 与 C(a[n],b[n])∗C(a[n−1],b[n−1])∗...∗C(a[0],b[0])%p 同余
即: Lucas(n,m,p)=c(n%p,m%p)∗Lucas(n/p,m/p,p)
应用于p较小但A,B较大的情况,将A,B降到 105 左右就可以应用逆元求组合数
4.欧拉函数
n的欧拉函数值
int euler_phi(int n){
int m = (int) sqrt(n+0.5);
int ans = n;
for(int i = 2; i <= m; i++) if(n % i == 0){
ans = ans / i * (i-1);
while(n % i == 0) n /= i;
}
if(n > 1) ans = ans / n * (n-1);//还剩一个大质数
return ans;
}//φ(n) = n(1-1/p1)(1-1/p2)(1-1/p3)...(1-1/pk)
1~n中所有数的欧拉phi函数值
void phi_table(int n, int *phi){
for(int i = 2; i <= n; i++) phi[i] = 0;
phi[1] = 1;
for(int i = 2; i <= n; i++) if(!phi[i])
for(int j = i; j <= n; j += i){
if(!phi[j]) phi[j] = j;
phi[j] = phi[j] / i * (i - 1);
}
}//一遍筛质数一边利用质因子算phi