基础数论复习笔记

目录

    • 欧几里得
      • 拓展欧几里得
        • 应用
    • 数论四大定理
      • 费马小定理
        • 应用
      • 欧拉定理
      • 威尔逊定理
      • 中国剩余定理
        • 孙子定理
        • 拓展中国剩余定理
    • 逆元
      • 拓欧求逆元
      • 费马小定理求逆元
      • 线性筛逆元
      • 线性筛阶乘逆元
    • 计数部分
      • 组合数线性求法
      • Lucas定理
      • 卡特兰数

欧几里得

gcd(a,b)=gcd(b,a%b)

拓展欧几里得

a ∗ x 0 + b ∗ y 0 = g c d ( a , b ) a*x_0+b*y_0=gcd(a,b) ax0+by0=gcd(a,b)

int exgcd(int a,int b,int &x,int &y) {
    if(b==0) {
        x=1,y=0;
        return a;
    }
    int x2,y2;
    int ret=exgcd(b,a%b,x2,y2);
    x=y2;
    y=x2-(a/b)*y2;
    return ret;
}

g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)

⇒ \Rightarrow a ⋅ x 1 + b ⋅ y 1 = b ⋅ x 2 + ( a % b ) ⋅ y 2 a·x1+b·y1=b·x2+(a\%b)·y2 ax1+by1=bx2+(a%b)y2

⇒ \Rightarrow a ⋅ x 1 + b ⋅ y 1 = b ⋅ x 2 + ( a − ⌊ a b ⌋ ⋅ b ) ⋅ y 2 a·x1+b·y1=b·x2+(a-\left\lfloor\dfrac{a}{b}\right\rfloor ·b)·y2 ax1+by1=bx2+(abab)y2

⇒ \Rightarrow a ⋅ x 1 + b ⋅ y 1 = a ⋅ y 2 + b ⋅ ( x 2 − ⌊ a b ⌋ ⋅ y 2 ) a·x1+b·y1=a·y2+b·(x2-\left\lfloor\dfrac{a}{b}\right\rfloor·y2) ax1+by1=ay2+b(x2bay2)

⇒ \Rightarrow x 1 = y 2 , y 1 = x 2 − ⌊ a b ⌋ ⋅ y 2 x1=y2,y1=x2-\left\lfloor\dfrac{a}{b}\right\rfloor·y2 x1=y2,y1=x2bay2

x ′ = x 0 + b g c d k x'=x0+\dfrac{b}{gcd}k x=x0+gcdbk , , , y ′ = y 0 − a g c d k y'=y0-\dfrac{a}{gcd}k y=y0gcdak

应用

1.解方程
2.求逆元
a ⋅ x ≡ 1 ( m o d    p ) a ⋅ x − p ⋅ k = 1 a·x≡1(mod\ \ p) \\ a·x-p·k=1 ax1(mod  p)axpk=1
p p p视作 b b b − k -k k视作 y y y
⇒ a ⋅ x + b ⋅ y = 1 \Rightarrow a·x+b·y=1 ax+by=1
解出 x 0 x0 x0 x ′ = x 0 + p ⋅ h x'=x0+p·h x=x0+ph求出合适的解

int INV(int a,int p) {
    int x,y;
    exgcd(a,p,x,y);
    x=(x%p+p)%p;
    return x;
}

数论四大定理

费马小定理

p p p为素数, g c d ( a , p ) = 1 , gcd(a,p)=1, gcd(a,p)=1, a p − 1 ≡ 1 ( m o d    p ) a^{p-1}≡1(mod\ \ p) ap11(mod  p)

应用

快速幂求逆元
由上 a p − 1 ≡ 1 ( m o d    p ) a^{p-1}≡1(mod\ \ p) ap11(mod  p)
⇒ a ⋅ a p − 2 ≡ 1 ( m o d    p ) \Rightarrow a·a^{p-2}≡1(mod\ \ p) aap21(mod  p)
⇒ a − 1 ≡ a p − 2 ( m o d    p ) \Rightarrow a^{-1}≡a^{p-2}(mod\ \ p) a1ap2(mod  p)

int mod;
LL Pow(int a,int k) {
    LL base=a,ret=1;
    while(k) {
        if(k%2)
            ret=(LL)(base*ret)%mod;
        base=(LL)(base*base)%mod;
        k/=2;
    }
    return ret;
}
LL INV(int a,int b) {
    mod=b;
    return Pow(a,b-2);
}

欧拉定理

是费马小定理的一般化,等考完还有想法再补补故事
g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1,则 a φ ( p ) ≡ 1 ( m o d    p ) a^{φ(p)}≡1(mod\ \ p) aφ(p)1(mod  p)
p p p为素数, φ ( p ) = p − 1 φ(p)=p-1 φ(p)=p1,就成了费马小定理

威尔逊定理

( p − 1 ) !   ≡ − 1 ( m o d    p ) (p−1)!\ ≡−1 (mod \ \ p) (p1)! 1(mod  p) ⇔ \Leftrightarrow p p p为素数
一般没什么用吧

中国剩余定理

为了区分

孙子定理

{ x ≡ a 1 ( m o d    m 1 ) x ≡ a 2 ( m o d    m 2 ) x ≡ a 3 ( m o d    m 3 ) … … x ≡ a n ( m o d    m n ) \begin{cases}\\ x≡a_1(mod \ \ m_1)\\ x≡a_2(mod \ \ m_2)\\ x≡a_3(mod \ \ m_3)\\ ……\\ x≡a_n(mod \ \ m_n) \end{cases} xa1(mod  m1)xa2(mod  m2)xa3(mod  m3)xan(mod  mn)
其中 m i m_i mi两两互质,求 x x x的解

使用构造法,令 M = Π m i M=Πm_i M=Πmi
求得 t i ≡ ( M m i ) − 1 ( m o d    m i ) t_i≡{\left(\dfrac{M}{m_i}\right)}^{-1} (mod \ \ m_i) ti(miM)1(mod  mi)
特解为 x 0 = ∑ a i ⋅ t i ⋅ ( M m i ) x_0=∑a_i·t_i·\left(\dfrac{M}{m_i}\right) x0=aiti(miM)
通解为 x ′ = x 0 + k ⋅ M x'=x_0+k·M x=x0+kM

LL Exgcd(LL a,LL b,LL &x,LL &y){
	if(!b){x=1,y=0;return a;}
	LL g=Exgcd(b,a%b,y,x);
	y-=(a/b)*x;
	return g;
}
int n;
LL a[MAXN+5],m[MAXN+5];
LL Mul(LL x,LL y,LL p){
	if(y<0) x=-x,y=-y;
	LL ret=0;
	while(y){
		if(y&1) ret=(ret+x)%p;
		x=(x<<1)%p,y>>=1;
	}
	return ret;
}
LL ExCRT(){
	LL M=m[1],x0=a[1],x,y,tmp;
	for(int i=2;i<=n;i++){
		LL g=Exgcd(M,m[i],x,y);
		if((a[i]-x0)%g) return -1;
		tmp=m[i]/g,x=Mul(x,(a[i]-x0)/g,tmp),x=(x+tmp)%tmp;
		x0+=M*x,M=M/g*m[i],x0%=M;
	}
	return x0;
}

该部分感谢LSK大佬
证明就把每一个对应部分分出来就行了,易知这是最小单位。

拓展中国剩余定理

{ x ≡ a 1 ( m o d    m 1 ) x ≡ a 2 ( m o d    m 2 ) x ≡ a 3 ( m o d    m 3 ) … … x ≡ a n ( m o d    m n ) \begin{cases}\\ x≡a_1(mod \ \ m_1)\\ x≡a_2(mod \ \ m_2)\\ x≡a_3(mod \ \ m_3)\\ ……\\ x≡a_n(mod \ \ m_n) \end{cases} xa1(mod  m1)xa2(mod  m2)xa3(mod  m3)xan(mod  mn)
不满足 m i m_i mi两两互质,求 x x x的解
使用逐个求解并合并的方法。

假设我们已经得到了一组特解 x 0 x_0 x0,如何得到一般解呢?
类比上文的孙子定理容易得到 x ′ = x 0 + k ⋅ l c m ( m 1 , m 2..... m n ) x'=x_0+k·lcm(m1,m2.....m_n) x=x0+klcm(m1,m2.....mn)

一个一个的向下解
M i = l c m ( m 1 , m 2 . . . . . . m i ) M_i=lcm(m_1,m_2......m_i) Mi=lcm(m1,m2......mi)
假如已经得到了前 i i i个方程的解 x = x 0 + k ⋅ M i ⇔ x ≡ x 0 ( m o d    M i ) x=x_0+k·M_i\Leftrightarrow x≡x_0(mod\ \ M_i) x=x0+kMixx0(mod  Mi)
现在解第 i + 1 i+1 i+1个方程,也就是再去求一组特解。
可以将问题简化为
{ x ≡ x 0 ( m o d    M i ) x ≡ a i + 1 ( m o d    m i + 1 ) \begin{cases} x≡x_0(mod\ \ M_i)\\ x≡a_{i+1}(mod\ \ m_{i+1}) \end{cases} {xx0(mod  Mi)xai+1(mod  mi+1)
可以写成

{ x = x 0 + k 1 ⋅ M i x = a i + k 2 ⋅ m i + 1 ⇒ x 0 + k 1 ⋅ M i = a i + k 2 ⋅ m i + 1 ⇒ x 0 − a i = − k 1 ⋅ M i + k 2 ⋅ m i + 1 \begin{cases} x=x_0+k_1·M_i\\ x=a_i+k_2·m_{i+1} \end{cases}\\ \Rightarrow x_0+k_1·M_i=a_i+k_2·m_{i+1}\\ \Rightarrow x_0-a_i=-k_1·M_i+k_2·m_{i+1} {x=x0+k1Mix=ai+k2mi+1x0+k1Mi=ai+k2mi+1x0ai=k1Mi+k2mi+1
因为 k 1 k1 k1的正负不重要
x 0 − a i = k 1 ⋅ M i + k 2 ⋅ m i + 1 x_0-a_i=k_1·M_i+k_2·m_{i+1} x0ai=k1Mi+k2mi+1
直接用拓欧解出一组 k 1 , k 2 k_1,k_2 k1,k2代入方程再更新 M i M_i Mi一直到最后就求出了方程的解。
中途用拓欧解方程时,若 g c d ( M i , m i + 1 ) ∤ x 0 − a i gcd(M_i,m_{i+1})\nmid{x0-a_i} gcd(Mi,mi+1)x0ai则无解

#include
#include
using namespace std;
const int MAXN=int(1e5+5);
int n,M=1,x0=0;
int a[MAXN],m[MAXN];
int exgcd(int a,int b,int &x,int &y) {
    if(b==0) {
        x=1,y=0;
        return a;
    }
    int x2,y2;
    int ret=exgcd(b,a%b,x2,y2);
    x=y2;
    y=x2-(a/b)*y2;
    return ret;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&m[i],&a[i]);
    for(int i=1;i<=n;i++) {
        int x,y;
        int G=exgcd(M,m[i],x,y);
        if((x0-a[i])%G) {
            puts("No solution");
            return 0;
        }
        x*=(x0-a[i])/G,y*=(x0-a[i])/G;
        x0=a[i]+y*m[i];
        M=M/G*m[i];
        x0=((x0%M)+M)%M;
    }
    printf("%d",x0);
}

逆元

拓欧求逆元

见上

费马小定理求逆元

见上

线性筛逆元

法1:求阶乘逆元反解(见下)
n ! − 1 = ( n − 1 ) ! − 1 ⋅ n − 1 n!^{-1}=(n-1)!^{-1}·n^{-1} n!1=(n1)!1n1
⇒ n − 1 = n ! − 1 ⋅ ( ( n − 1 ) ! − 1 ) − 1 = n ! − 1 ⋅ ( n − 1 ) ! \Rightarrow n^{-1}=n!^{-1}·((n-1)!^{-1})^{-1}=n!^{-1}·(n-1)! n1=n!1((n1)!1)1=n!1(n1)!

int Pow(int a,int k) {
    int base=a,ret=1;
    while(k) {
        if(k%2)
            ret=((LL)ret*base)%p;
        base=((LL)base*base)%p;
        k/=2;
    }
    return ret;
}
void Prepare() {
    fac[0]=1;
    for(int i=1;i<=n;i++)
        fac[i]=((LL)fac[i-1]*i)%p;
    inv1[n]=Pow(fac[n],p-2);
    for(int i=n;i>=1;i--)
        inv1[i-1]=((LL)inv1[i]*i)%p;
    for(int i=1;i<=n;i++)
        inv2[i]=((LL)inv1[i]*fac[i-1])%p;
}

法2:真正的线性筛逆元
p = k ⋅ i + d p=k·i+d p=ki+d其中 d = p % i d=p\%i d=p%i
那么 k ⋅ i + d ≡ 0 ( m o d    p ) k·i+d≡0(mod\ \ p) ki+d0(mod  p)
⇒ − k ⋅ i ≡ d \Rightarrow -k·i≡d kid
⇒ − k ⋅ i ⋅ i − 1 ⋅ d − 1 ≡ d ⋅ i − 1 ⋅ d − 1 \Rightarrow -k·i·i^{-1}·d^{-1}≡d·i^{-1}·d^{-1} kii1d1di1d1
⇒ i − 1 ≡ − k ⋅ d − 1 \Rightarrow i^{-1}≡-k·d^{-1} i1kd1
⇒ i − 1 = ( p − k ) ⋅ d − 1 \Rightarrow i^{-1}=(p-k)·d^{-1} i1=(pk)d1

void Prepare() {
    inv[1]=1;
    for(int i=2;i<=n;i++)
        inv[i]=(p-p/i)*inv[p%i]%p;
}

线性筛阶乘逆元

用定义理解,先求出 ( p − 1 ) ! − 1 {(p-1)!}^{-1} (p1)!1(可先求阶乘,或直接用威尔逊定理)
p p p是素数, ( p − 1 ) ! ≡ − 1 ( m o d    p ) (p-1)!≡-1(mod\ \ p) (p1)!1mod  p)
那么 ( p − 1 ) ! − 1 = ( p − 1 ) − 1 {(p-1)!}^{-1}=(p-1)^{-1} (p1)!1=(p1)1直接快速幂。
或者直接算出 ( p − 1 ) ! (p-1)! (p1)!反正都要算。
逆元满足结合律即 a − 1 ⋅ b − 1 = ( a b ) − 1 a^{-1}·b^{-1}=(ab)^{-1} a1b1=(ab)1
那么 i n v [ n ] = i n v [ n − 1 ] ⋅ n − 1 inv[n]=inv[n-1]·n^{-1} inv[n]=inv[n1]n1
⇒ i n v [ n ] ⋅ n = i n v [ n − 1 ] ⋅ n − 1 ⋅ n = i n v [ n − 1 ] \Rightarrow inv[n]·n=inv[n-1]·n^{-1}·n=inv[n-1] inv[n]n=inv[n1]n1n=inv[n1]

void Prepare() {
    inv[p-1]=Pow(p-1,p-2);
    for(int i=p;i>=1;i--)
        inv[i-1]=inv[i]*i%p;
}

当然也是可以先求出线性逆元再用线性逆元求出来。

计数部分

组合数线性求法

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(n1,m)+C(n1,m1)

void Prepare() {
    C[0][0]=C[1][0]=C[1][1]=1;
    for(int i=1;i<=MAXN;i++)
        for(int j=1;j<=i;j++)
            C[i][j]=C[i-1][j]+C[i-1][j-1];
}

Lucas定理

C ( n , m ) % p = C ( n / p , m / p ) ⋅ C ( n % p , m % p ) % p C(n,m)\%p=C(n/p,m/p)·C(n\%p,m\%p)\%p C(n,m)%p=C(n/p,m/p)C(n%p,m%p)%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
1.当 p p p较小
预处理组合数,直接递归到已处理的数

void Prepare() {
    C[0][0]=C[1][0]=C[1][1]=1;
    for(int i=1;i<=MAXN;i++)
        for(int j=1;j<=i;j++)
            C[i][j]=C[i-1][j]+C[i-1][j-1];
}
int Lucas(int n,int m) {
    if(n<p&&m<p)
        return C[n][m];
    return Lucas(n/p,m/p)*Lucas(n%p,m%p)%p;
}

3.当 p p p较大
预处理阶乘和阶乘的逆元(见上),对小的部分直接算,大的部分继续递归处理

void Prepare() {
    inv[p-1]=Pow(p-1,p-2);
    for(int i=p-1;i>=1;i--)
        inv[i-1]=inv[i]*i%p;
    fac[0]=1;
    for(int i=1;i<=p;i++)
        fac[i]=fac[i-1]*i%p;
}
int C(int n,int m) {
    if(n<m)
        return 0;
    return ((fac[n]*inv[m])%p*inv[n-m])%p;
}
int Lucas(int n,int m) {
    if(n<p&&m<p)
        return C(n,m);
    return Lucas(n/p,m/p)*C(n%p,m%p)%p;
}

卡特兰数

组合意义(经典例子):
1.括号匹配( n n n对括号的合法匹配数)
2.走地图(从 ( 0 , 0 ) (0,0) (0,0)走到 ( n , n ) (n,n) (n,n)并且不越过 y = x y=x y=x的方案数)
公式: h ( n ) = C ( 2 n , n ) n + 1 h(n)=\dfrac{C(2n,n)}{n+1} h(n)=n+1C(2n,n)
推导:在一个 ( n , n ) (n,n) (n,n)的地图里,从 ( 0 , 0 ) (0,0) (0,0)走到 ( n , n ) (n,n) (n,n)的方案数为 C ( 2 n , n ) C(2n,n) C(2n,n)
要求不越过 y = x y=x y=x,那么求越过的取反。
越过的就是一定经过 y = x + 1 y=x+1 y=x+1
观察到这样的走法等价于从 ( − 1 , 1 ) (-1,1) (1,1)走到 ( n , n ) (n,n) (n,n)的方案数(将地图外的部分沿 y = x + 1 y=x+1 y=x+1翻折回来)。
图示
基础数论复习笔记_第1张图片

你可能感兴趣的:(查来查去写笔记,算进算出最美丽,NOIP)