数论
1.1 Lucas定理 (扩展Lucas定理)【组合数取模】
Lucas定理:
要计算 C n m % p C_n^m \%p Cnm%p的值
n = n k p k + n k − 1 p k − 1 + . . . + n 1 p + n 0 n = n_kp^k+n_{k-1}p^{k-1}+...+n_1p+n_0 n=nkpk+nk−1pk−1+...+n1p+n0m = m k p k + m k − 1 p k − 1 + . . . + m 1 p + m 0 m = m_kp^k+m_{k-1}p^{k-1}+...+m_1p+m_0 m=mkpk+mk−1pk−1+...+m1p+m0
C n m = ∏ i = 0 k C n i m i ( m o d p ) C_n^m = \prod^k_{i = 0}C_{n_i}^{m_i}(mod\ p) Cnm=i=0∏kCnimi(mod p)
即将n.m分解为p进制,然后求每一位上的组合数,再乘起来,就是答案
- 1、当n,m都很小的时候可以利用杨辉三角直接求。
- C ( n , m ) = C ( n − 1 , m ) + C ( n − 1 , m − 1 ) C(n,m)=C(n-1,m)+C(n-1,m-1) C(n,m)=C(n−1,m)+C(n−1,m−1)
- 2、n和m较大,但是p为素数的时候,递归求解:
- L u c a s ( n , m ) % p = L u c a s ( n / p , m / p ) ∗ C ( n % p , m % p ) % p Lucas(n,m) \%p = Lucas(n/p,m/p) * C(n\%p,m\%p)\%p Lucas(n,m)%p=Lucas(n/p,m/p)∗C(n%p,m%p)%p
- 递归出口:m=0时返回1
- p较小时,可以预处理阶乘
LL qpow(LL a, LL b, LL p)//快速幂取模
{
LL ans = 1;
a %= p;
while(b)
{
if(b & 1)
{
ans = ans * a % p;
b--;
}
b >>= 1;
a = a * a % p;
}
return ans;
}
LL inv(LL a, LL p)//求逆元,要求p为素数,且a和m互质
{
return qpow(a, p - 2, p);
}
LL C(LL n, LL m)//求组合数
{
if(m > n)
return 0;
LL ans = 1;
for(int i = 1; i <= m; i++)
{
LL a = (n + i - m) % p;
LL b = i % p;
ans = ans * (a * inv(b, p) % p) % p;
}
return ans;
}
LL Lucas(LL n, LL m)//求组合数取模
{
if(m == 0) return 1;
return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}
//打表预处理组合数,可以加快阶乘计算
void getfac(int p)//阶乘表
{
fac[0] = fac[1] = 1;
for(int i = 2 ; i <= p; i++)
fac[i] = fac[i - 1] * i % p;
}
LL C(LL n, LL m, LL p) //组合数
{
if(m > n)
return 0;
else
return fac[n] * inv(fac[m] * fac[n - m], p) % p;
}
n,m较大且p不为素数的时候,扩展Lucas定理
令 p = p 1 k 1 ∗ p 2 k 2 ∗ . . . ∗ p q k q p=p_1^{k_1}*p_2^{k_2}*...*p_q^{k_q} p=p1k1∗p2k2∗...∗pqkq( p i p_i pi是质数)
列出同余方程组 { a n s ≡ c 1 m o d p 1 k 1 a n s ≡ c 2 m o d p 2 k 2 . . . . . . a n s ≡ c n m o d p n k n \begin{cases} ans \equiv c_1 \ mod \ p_1^{k_1} \\ ans \equiv c_2 \ mod \ p_2^{k_2} \\...... \\ans \equiv c_n \ mod \ p_n^{k_n} \end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧ans≡c1 mod p1k1ans≡c2 mod p2k2......ans≡cn mod pnkn
其中 c i . . . c q c_i...c_q ci...cq是对于每一个 C n m % p i k i C_n^m \% p_i^{k_i} Cnm%piki求出的答案
使用中国剩余定理即可求出ans。
其中 C n m % p i k i C_n^m \% p_i^{k_i} Cnm%piki的求法关键在于 n ! % p i k i n! \% p_i^{k_i} n!%piki的求法,以下给出解法:
- n ! % p i k i n! \% p_i^{k_i} n!%piki是三部分的乘积
- 第一部分是 p i ⌊ n p i ⌋ p_i^{\lfloor \frac{n}{p_i} \rfloor} pi⌊pin⌋
- 第二部分是 ⌊ n p i ⌋ ! \lfloor \frac{n}{p_i} \rfloor ! ⌊pin⌋!
- 第三部分是上面的数之外的其他数(不超过 p i k i p_i^{k_i} piki)的乘积,可暴力
m ! % p i k i m! \% p_i^{k_i} m!%piki和 ( n − m ) ! % p i k i (n-m)! \% p_i^{k_i} (n−m)!%piki的结果可能与 p i k i p_i^{k_i} piki不互质,无法求逆元,所以要先去除其中的质因子 p i p_i pi,求出逆元后,再乘回去。
计算 n ! n! n!中的质因子p的个数x: x = ⌊ n p ⌋ + ⌊ n p 2 ⌋ + . . . x=\lfloor \frac{n}{p} \rfloor + \lfloor \frac{n}{p^2} \rfloor+... x=⌊pn⌋+⌊p2n⌋+...
递推式为: f ( n ) = f ( ⌊ n p ⌋ ) + ⌊ n p ⌋ f(n)=f(\lfloor \frac{n}{p} \rfloor)+\lfloor \frac{n}{p} \rfloor f(n)=f(⌊pn⌋)+⌊pn⌋
LL qpow(LL a, LL n, LL m)//快速幂
{
LL ans = 1;
a %= m;
while(n)
{
if(n & 1)
ans = ans * a % m;
n >>= 1;
a = a * a % m;
}
return ans;
}
//求解ax+by=gcd(a, b)
//返回值为gcd(a, b)
LL exgcd(LL a, LL b, LL& x, LL& y)//扩展欧几里得
{
LL d = a;
if(b)
{
d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
else x = 1, y = 0;
return d;
}
//求解a关于模上m的逆元
//返回-1表示逆元不存在
LL inv(LL a, LL m)
{
LL x, y;
LL d = exgcd(a, m, x, y);
return d == 1 ? (m + x % m) % m : -1;
}
//计算n! mod p^k的部分值,pk为p_i的k_i次方,算出的答案不包括pi的幂的那一部分
LL Mul(LL n, LL pi, LL pk)
{
if(!n) return 1;
LL ans = 1;
if(n / pk)
{
for(LL i = 2; i <= pk; i++) //求出循环节乘积
if(i % pi) ans = ans * i % pk;
ans = qpow(ans, n / pk, pk); //循环节次数为n / pk
}
for(LL i = 2; i <= n % pk; i++)
if(i % pi) ans = ans * i % pk;
return ans * Mul(n / pi, pi, pk) % pk;//递归求解
}
LL C(LL n, LL m, LL p, LL pi, LL pk)//计算组合数C(n, m) mod p^k的值 p^k为p_i的k-i次方
{
if(m > n) return 0;
LL a = Mul(n, pi, pk), b = Mul(m, pi, pk), c = Mul(n - m, pi, pk);
LL k = 0, ans;//k为pi的幂值
for(LL i = n; i; i /= pi) k += i / pi;
for(LL i = m; i; i /= pi) k -= i / pi;
for(LL i = n - m; i; i /= pi) k -= i / pi;
ans = a * inv(b, pk) % pk * inv(c, pk) % pk * qpow(pi, k, pk) % pk;//ans就是n! mod pk的值
ans = ans * (p / pk) % p * inv(p / pk, pk) % p;//此时用剩余定理合并解
return ans;
}
LL Lucas(LL n, LL m, LL p)//求组合数C(n, m) mod p的值,p为合数
{
LL x = p;
LL ans = 0;
for(LL i = 2; i <= p; i++)
{
if(x % i == 0)
{
LL pk = 1;
while(x % i == 0)pk *= i, x /= i;//将p分解成质数幂的乘积
ans = (ans + C(n, m, p, i, pk)) % p;//剩余定理求解
}
}
return ans;
}
1.2 扩展欧几里得
求 a ∗ x + b ∗ y = g c d ( a , b ) a*x+b*y=gcd(a, b) a∗x+b∗y=gcd(a,b)的解
//求解ax+by=gcd(a, b)
//返回值为gcd(a, b)
LL exgcd(LL a, LL b, LL& x, LL& y)
{
LL d = a;
if(b)
{
d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
}
else x = 1, y = 0;
return d;
}
1.3 威尔逊定理
p可以整除(p-1)!+1是p为质数的充要条件
1.4 欧拉定理(扩展欧拉定理)【高次幂:对指数取模(模无限制)】
- 欧拉定理:
若a,m为整数,且a,m互质,则 a ϕ ( m ) ≡ 1 ( m o d m ) a^{\phi(m)} \equiv 1\ (mod\ m) aϕ(m)≡1 (mod m)
- 扩展欧拉定理:
a c ≡ { a c m o d ϕ ( m ) , g c d ( a , m ) = 1 a c , g c d ( a , m ) ≠ 1 , c < ϕ ( m ) a ( c m o d ϕ ( m ) ) + ϕ ( m ) , g c d ( a , m ) ≠ 1 , c ≥ ϕ ( m ) a^c \equiv\begin{cases}a^{c \ mod \ \phi(m)}, & gcd(a, m)=1\\ a^c, & gcd(a, m) \neq 1,c < \phi(m) \\a^{(c \ mod \ \phi(m))+\phi(m)}, & gcd(a, m) \neq 1,c \geq \phi(m) \end{cases} ac≡⎩⎪⎨⎪⎧ac mod ϕ(m),ac,a(c mod ϕ(m))+ϕ(m),gcd(a,m)=1gcd(a,m)̸=1,c<ϕ(m)gcd(a,m)̸=1,c≥ϕ(m)
- 因此求高次幂,无论a,n是否互质,都可以使用 a c ≡ a ( c m o d ϕ ( m ) ) + ϕ ( m ) m o d m a^c \equiv a^{(c \ mod \ \phi(m))+\phi(m)} \ mod \ m ac≡a(c mod ϕ(m))+ϕ(m) mod m
1.5 费马小定理【高次幂:对指数取模(模为质数)】
m为质数
- a不为m的倍数时, a ( m − 1 ) ≡ 1 ( m o d m ) a^{(m-1)} \equiv 1 \ (mod \ m) a(m−1)≡1 (mod m)
- a为m的倍数时, a ( m − 1 ) ≡ 0 ( m o d m ) a^{(m-1)} \equiv 0 \ (mod \ m) a(m−1)≡0 (mod m)
- 因此求高次幂,可以用该定理对指数取模
1.6 中国剩余定理
求线性同余方程组
{ X ≡ d 1 m o d p 1 X ≡ d 2 m o d p 2 . . . . . . X ≡ d n m o d p n \begin{cases} X \equiv d_1 \ mod \ p_1 \\ X \equiv d_2 \ mod \ p_2 \\...... \\X \equiv d_n \ mod \ p_n \end{cases} ⎩⎪⎪⎪⎨⎪⎪⎪⎧X≡d1 mod p1X≡d2 mod p2......X≡dn mod pn
求X的值
const int N = 15;
int n;
LL yu[N], mo[N];
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
LL r = exgcd(b, a % b, x, y);
LL tmp = y;
y = x - (a / b) * y;
x = tmp;
return r;
}
LL CRT(LL *yu, LL *mo, int num)
{
LL M = 1, ans = 0, x, y;
for(int i = 0; i < num; i++)
M *= mo[i];
for(int i = 0; i < num; i++)
{
LL w = M / mo[i];
exgcd(w, mo[i], x, y);
ans = (ans + yu[i] * x * w) % M;
}
return (ans + M) % M;
}