数学学习笔记

质数

  • 又称为素数
1 - 定义
  • 在大于 1 1 1 的整数中,只包含 1 1 1 和本身这两个约数的数
2 - 判定

试除法,根据定义来判定

bool is_prime(int n) {
    for(int i=2;i<n;i++) {
        if(x % i == 0) return 0;
    }
    return 1;
}

优化:

  • 首先,我们可以由 d ∣ n d\mid n dn 推导 n d ∣ n \frac{n}{d} \mid n dnn
  • 所以我们在枚举约数时只需要枚举 d ≤ n d d \le \frac{n}{d} ddn 的部分,即 d ≤ n d \le \sqrt n dn
bool is_prime(int n) {
    for(int i=2;i<=n/i;i++) {
        if(n % i == 0) return 0;
    }
    return 1;
}

时间复杂度由 O ( n ) O(n) O(n) 降为 O ( n ) O(\sqrt n) O(n )

3 - 分解质因数

试除法,从小到大枚举 n n n 的所有因数

void divide(int n) {
    for(int i=2;i<=n;i++) 
        if(n % i == 0) {
            int cnt = 0;
            while(n % i == 0) {
                n /= i;
                cnt++;
            }
            cout << i << " " << cnt << endl; // 输出底数和指数
        }
    }
}

优化:

  • n n n 当中最多只包含一个大于 n \sqrt n n 的质因子
  • 枚举时先举出小于等于 n \sqrt n n 的质因子,最后大于 n \sqrt n n 的质因子特判即可
void divide(int n) {
    for(int i=2;i<=n;i++) 
        if(n % i == 0) {
            int cnt = 0;
            while(n % i == 0) {
                n /= i;
                cnt++;
            }
            cout << i << " " << cnt << endl;
        }
    }
	if(n > 1) cout << n << " " << 1 << endl; // 特判
}

时间复杂度由 O ( n ) O(n) O(n) 降为 O ( n ) O(\sqrt n) O(n )

4 - 筛质数

即把质数从一定的范围内快速的选出

埃氏筛

2 2 2 ~ n n n 中筛质数,每次删除 2 、 3 … n − 1 2、3 … n-1 23n1 的倍数,剩下的为质数

原理:

  • 对于 p i p_i pi ,若它在第 i − 1 i-1 i1 轮没有被删掉,说明 2 、 3 … n − 1 2、3…n-1 23n1 都不是 p i p_i pi 的因数,即 p i p_i pi 只有 1 1 1 n n n 这两个因数
void get_primes(int n) {
    for(int i=2;i<=n;i++) {
        if(!t[i]) {
            primes[ans++] = i; // 记录质数
        }
        for(int j=i+i;j<=n;j+=i) t[j] = 1; // 删掉 i 的倍数
    }
}

优化:

  • 合数是质数的倍数,所以合数的倍数不用再删一遍
void get_primes(int n) {
    for(int i=2;i<=n;i++) {
        if(!t[i]) {
            primes[ans++] = i;
            for(int j=i+i;j<=n;j+=i) t[j] = 1;
        }
    }
}

时间复杂度由 O ( n log ⁡ n ) O(n \log n) O(nlogn) 降为 O ( n log ⁡ log ⁡ n ) O(n\log \log n) O(nloglogn) ,几乎为 O ( n ) O(n) O(n)

线性筛

原理:

  • 用每个合数的最小质因子筛掉它

  • 遍历质数列表,依次筛掉 p j p_j pj i i i

    • i m o d    j = 0 i\mod j=0 imodj=0 ,因为是从小到大遍历的,所以 p j p_j pj 一定是 i i i 的最小质因子,也是 p j × i p_j \times i pj×i 的最小质因子

    • i m o d    j ≠ 0 i \mod j \neq 0 imodj=0,同上, p j p_j pj 一定小于 i i i 的所有质因子, 也是 p j × i p_j \times i pj×i 的最小质因子

void get_primes(int n) {
    for(int i=2;i<=n;i++) {
        if(!t[i]) primes[ans++] = i;
        for(int j=0;primes[j]<=n/i;j++) { // 枚举每个质数
            t[primes[j] * i] = 1; // 筛,见上
            if(i % primes[j] == 0) break;
        }
    }
}

时间复杂度为 O ( n ) O(n) O(n) ,比埃筛稍快

约数

1 - 求约数

试除法

优化:

  • d ∣ n d \mid n dn 推导 n d ∣ n \frac{n}{d} \mid n dnn
  • 所以枚举较小的约数,另一个直接算
  • 枚举 d ≤ n d d \le \frac{n}{d} ddn ,即 d ≤ n d \le \sqrt{n} dn
void get_divisors(int n) {
    for(int i=1;i<=n/i;i++) {
        if(n % i == 0) {
            ans[++cnt] = i;
            if(i != n / i) ans[++cnt] = n / i;
        }
    }
}

时间复杂度为 O ( n ) O(\sqrt n) O(n )

2 - 约数个数

用约数个数定理

  • 对于一个大于 1 1 1 的正整数 n n n ,可分解为: p 1 a 1 ⋅ p 2 a 2 … p k a k {p_1}^{a_1}\cdot {p_2}^{a_2}…{p_k}^{a_k} p1a1p2a2pkak
  • p i a i {p_i}^{a_i} piai 有约数 p i 0 , p i 1 , p i 2 , … p i a i {p_i}^0,{p_i}^1,{p_i}^2,…{p_i}^{a_i} pi0,pi1,pi2,piai ,共 a i + 1 a_i+1 ai+1
  • 所以 n n n 共有 ( a 1 + 1 ) ( a 2 + 1 ) … ( a i + 1 ) (a_1+1)(a_2+1)…(a_i+1) (a1+1)(a2+1)(ai+1) 个约数

同余

  • 如果两个数 a a a b b b 之差能被 m m m 整除,那么我们就称 a a a b b b 对模数 m m m 同余
  • 记作 a ≡ b ( m o d m ) a\equiv b\pmod m ab(modm)
1 - 性质
  • 自反性(一个数永远和自己同余)
  • 对称性( a a a b b b 同余, b b b a a a 也就同余)
  • 传递性( a a a b b b 同余, b b b c c c 同余可以推出 a a a c c c 同余)
2 - 推论
  • 如果 a ≡ b ( m o d m ) , x ≡ y ( m o d m ) a\equiv b\pmod m,x\equiv y\pmod m ab(modm),xy(modm),则 a ± x ≡ b ± y ( m o d m ) a\pm x\equiv b\pm y\pmod m a±xb±y(modm)
  • 如果 a ≡ b ( m o d m ) , x ≡ y ( m o d m ) a\equiv b\pmod m,x\equiv y\pmod m ab(modm),xy(modm),则 a x ≡ b y ( m o d m ) ax\equiv by\pmod m axby(modm)
  • 如果 a c ≡ b c ( m o d m ) ac\equiv bc\pmod m acbc(modm),且 c c c m m m 互质,则 a ≡ b ( m o d m ) a\equiv b\pmod m ab(modm)
  • 如果 a ≡ b ( m o d m ) a\equiv b\pmod m ab(modm),且 c ≠ 0 c\ne 0 c=0,则 a c ≡ b c ( m o d m c ) ac\equiv bc\pmod {mc} acbc(modmc)

费马小定理

如果 p p p 是质数,且 a , p a,p a,p 互质,有:
a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod p ap11(modp)

应用

求乘法逆元

( x × y ) ≡ 1 ( m o d p ) (x\times y)\equiv 1\pmod p (x×y)1(modp) ,则 y y y x x x p p p 的乘法逆元

  • 因为 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\pmod p ap11(modp) ,所以 ( a × a p − 2 ) ≡ 1 ( m o d p ) (a\times a^{p-2})\equiv 1\pmod p (a×ap2)1(modp)

  • a p − 2 a^{p-2} ap2 a a a 关于模 p p p 的乘法逆元

a b m o d    p = ( a m o d    p ) ( b ′ m o d    p ) m o d    p \frac{a}{b}\mod p=(a\mod p)(b'\mod p)\mod p bamodp=(amodp)(bmodp)modp

其中 p p p 为质数, b ′ b' b b b b 的乘法逆元

欧拉函数

  • 对于一个正整数 x x x ,小于 x x x 且和 x x x 互质的正整数的个数表示为 φ ( x ) \varphi (x) φ(x)

  • φ ( 1 ) \varphi (1) φ(1) 的值为 1 1 1 ,没有实际意义

1 - 通式
  • φ ( x ) = x ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) … ( 1 − 1 p n ) \varphi (x)=x(1-\frac{1}{p_1})(1-\frac{1}{p_2})…(1-\frac{1}{p_n}) φ(x)=x(1p11)(1p21)(1pn1)

    其中, p 1 , p 2 … , p n p_1,p_2…,p_n p1,p2,pn x x x 的所有质因数

  • φ ( x ) = p k − p k − 1 \varphi (x)=p^k-p^{k-1} φ(x)=pkpk1

    其中, x = p k x=p^k x=pk p p p 为质数

2 - 性质
  • 欧拉函数是积性函数

    x , y x,y x,y 互质,则 φ ( x × y ) = φ ( x ) × φ ( y ) \varphi(x\times y)=\varphi(x)\times \varphi(y) φ(x×y)=φ(x)×φ(y)

  • x x x 是质数,则 φ ( x ) = x − 1 \varphi(x)=x-1 φ(x)=x1

欧拉定理

a , n a,n a,n 为正整数,且 a , n a,n a,n 互质,有:
a φ ( n ) ≡ 1 ( m o d n ) a^{\varphi(n)}\equiv 1\pmod n aφ(n)1(modn)

1 - 应用

降幂,当 a , n a,n a,n 互质时:
a b m o d    n = a b m o d    φ ( n ) m o d    n a^b\mod n=a^{b\mod \varphi(n)}\mod n abmodn=abmodφ(n)modn

2 - 扩欧

b ≥ φ ( c ) b\ge \varphi(c) bφ(c) 时:
a b m o d    c = a b m o d    φ ( c ) + φ ( c ) m o d    c a^b\mod c=a^{b\mod \varphi(c)+\varphi(c)}\mod c abmodc=abmodφ(c)+φ(c)modc

3 - 代码
  • φ ( x ) \varphi (x) φ(x)

    int phi(int x) {
        int ans = x;
        for(int i=2;i<=x/i;i++) {
            if(x % i == 0) {
                ans -= ans/i;
                while(x % i == 0) x /= i;
            }
        }
        if(x > 1) ans -= ans / x;
        return ans;
    }
    

    时间复杂度 O ( n ) O(\sqrt n) O(n )

  • φ ( 1 ) , φ ( 2 ) … , φ ( n ) \varphi(1),\varphi(2)…,\varphi(n) φ(1),φ(2),φ(n)

    void phi(int n) {
        for(int i=1;i<=n;i++) p[i] = i;
        for(int i=2;i<=n;i++) {
            if(p[i] == i) {
                for(int j=i;j<=n;j+=i) {
                    p[j] = p[j] / i * (i - 1);
                }
            }
        }
    }
    

    时间复杂度 O ( n log ⁡ log ⁡ n ) O(n\log\log n) O(nloglogn)

扩展欧几里得算法

在已知整数 a , b a,b a,b 的情况下,求 a x + b y = gcd ⁡ ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的整数解

1 - 解法
  • 该方程一定有解

  • x = y ′ y = x ′ − a b × y x=y'\\ y=x'-\frac{a}{b}\times y x=yy=xba×y

    到最后可得 x ′ = 1 , y ′ = 0 x'=1,y'=0 x=1,y=0,于是逐步往前推即可

2 - 代码
int k_ou(int a,int b,int &x1,int &y1) {
    if(b == 0) {
        x1 = 1,y1 = 0;
        return a;
    }
    int x2,y2;
    int d = k_ou(b,a % b,x2,y2);
    x1 = x2,y1 = x2 - a / b * y2;
    return d;
}
int k_ou(int a,int b,int &x,int &y) {
    if(b == 0) {
        x = 1,y = 0;
        return a;
    }
    int d = k_ou(b,a % b,x,y);
    int temp = x;
 	x = y,y = temp - a / b * y;
    return d;
}
3 - 应用
不定方程
  • 对于不定方程 a x + b y = c ax+by=c ax+by=c ,扩欧算法可以判断是否有解,并求出通解

  • 如果 c m o d    d ≠ 0 c\mod d\ne0 cmodd=0 那么该方程无解(其中 d d d 可为 gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b)

  • c m o d    d = 0 c\mod d=0 cmodd=0 时,即该方程有解时:

    • d = gcd ⁡ ( a , b ) d=\gcd(a,b) d=gcd(a,b) ,因此可用扩欧求出 a x ′ + b y ′ = d = gcd ⁡ ( a , b ) ax'+by'=d=\gcd(a,b) ax+by=d=gcd(a,b) 的解 x ′ , y ′ x',y' x,y

    • 则原方程的一组 x = x ′ × c d , y = y ′ × c d x=x'\times \frac{c}{d},y=y'\times \frac{c}{d} x=x×dc,y=y×dc

    • 而所有的解可表示为:
      x = x ′ × c d + k × b d y = y ′ × c d − k × a d x=x'\times \frac{c}{d}+k\times \frac{b}{d}\\ y=y'\times \frac{c}{d}-k\times \frac{a}{d} x=x×dc+k×dby=y×dck×da
      其中 k k k 为任意整数

模线性方程
  • 解形如 a x ≡ b ( m o d n ) ax\equiv b\pmod n axb(modn) 的方程
  • 根据模的定义,可得 a x − b = n k ax-b=nk axb=nk k k k 为整数
  • 移项得: a x − n k = b ax-nk=b axnk=b ,可用扩欧解决

中国剩余定理

  • w 1 , w 2 … , w n w_1,w_2…,w_n w1,w2,wn两两互质的正整数

    有方程:
    { x ≡ b 1 ( m o d w 1 ) x ≡ b 2 ( m o d w 2 ) … x ≡ b n ( m o d w n ) \begin{equation} \left\{ \begin{array}{lr} x\equiv b_1\pmod{w_1}\\ x\equiv b_2\pmod{w_2}\\ …\\ x\equiv b_n\pmod{w_n}\\ \end{array} \right. \end{equation} xb1(modw1)xb2(modw2)xbn(modwn)

  • 上面方程的解为
    x = ( m 1 × m 1 − 1 × b 1 + m 2 × m 2 − 1 … + m n × m n − 1 × b n ) m o d    p x=(m_1\times m_1^{-1}\times b_1+m_2\times m_2^{-1}…+m_n\times m_n^{-1}\times b_n)\mod p x=(m1×m11×b1+m2×m21+mn×mn1×bn)modp

  • 其中, p = w 1 × w 2 … × w n p=w_1\times w_2…\times w_n p=w1×w2×wn

    m i = p w i m_i=\frac{p}{w_i} mi=wip

    m i − 1 m_i^{-1} mi1 m i m_i mi w i w_i wi 的乘法逆元,即 m i × m i − 1 ≡ 1 ( m o d w i ) m_i \times m_i^{-1}\equiv 1\pmod {w_i} mi×mi11(modwi)

  • 怎么求乘法逆元?

    m i × m i − 1 − w i × k = 1 m_i\times m_i^{-1}-w_i\times k=1 mi×mi1wi×k=1

    扩欧求解即可

代码
int china(int b[],int w[],int n) {
    int ans = 0,p = 1;
    for(int i=1;i<=n;i++) p *= w[i];
    for(int i=1;i<=n;i++) {
        int mi = p / w[i];
        int d = k_ou(mi,w[i],x,y);
        ans = (ans + x * mi * b[i]) % p;
    }
    if(ans > 0) return ans;
    else return ans + p;
}

组合数学

1 - 排列组合
  • n n n 个不同的元素中,取 m m m 个不重复的元素,按次序排列,即为排列
    A n m = n ! ( n − m ) ! A_n^m=\frac{n!}{(n-m)!} Anm=(nm)!n!

  • n n n 个不同的元素中,取 m m m 个不重复的元素,不考虑顺序,即为组合
    C n m = n ! ( n − m ) !   m ! C_n^m=\frac{n!}{(n-m)!\ m!} Cnm=(nm)! m!n!

  • 组合数的性质:
    C n m = C n n − m C_n^m=C_n^{n-m} Cnm=Cnnm

    C n m = C n − 1 m + C n − 1 m − 1 C_n^m=C_{n-1}^m+C_{n-1}^{m-1} Cnm=Cn1m+Cn1m1

    C n 0 + C n 1 … + C n n = 2 n C_n^0+C_n^1…+C_n^n=2^n Cn0+Cn1+Cnn=2n

斯特林数

1 - 第一类斯特林数
  • s 1 n , m s1_{n,m} s1n,m 表示把 n n n 个元素划分为 m m m非空循环排列集合的方案数

  • S 1   n , m = S 1   n − 1 , m − 1 + ( n − 1 ) × S 1   n − 1 , m S_{1\ n,m}=S_{1\ n-1,m-1}+(n-1)\times S_{1\ n-1,m} S1 n,m=S1 n1,m1+(n1)×S1 n1,m

void get() {
    for(int i=0;i<=n;i++) s1[i][i] = 1;
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=m&&j<=i;j++) {
            s1[i][j] = (s[i - 1][j - 1] + (i - 1) * s1[i - 1][j]) % mod;
        }
    }
}
2 - 第二类斯特林数
  • s 2 n , m s2_{n,m} s2n,m 来表示把 n n n 个元素划分成 m m m 个非空集合的方案数

  • S 2   n , m = S 2   n − 1 , m − 1 + m × S 2   n − 1 , m S_{2\ n,m}=S_{2\ n-1,m-1}+m\times S_{2\ n-1,m} S2 n,m=S2 n1,m1+m×S2 n1,m

void get() {
    for(int i=1;i<=n;i++) s2[i][1] = 1;
    for(int i=1;i<=n;i++) {
        for(int j=2;j<=i&&j<=m;j++) {
            s2[i][j] = (s2[i - 1][j - 1] + j * s2[i - 1][j]) % mod;
        }
    }
}
3 - BELL数
  • BELL 数 b n b_n bn 表示把 n n n 个元素划分为若干个非空集合的方案数

  • b n = S 2   n , 1 + S 2 ,   n , 2 … + S 2   n , n b_n=S_{2\ n,1}+S_{2,\ n,2}…+S_{2\ n,n} bn=S2 n,1+S2, n,2+S2 n,n

Lucas 定理

  • 计算 C n m m o d    p C_n^m\mod p Cnmmodp p p p 是质数

  • L u c a s ( n , m , p ) = C n m o d    p m m o d    p × L u c a s ( a p , b p , p ) Lucas(n,m,p)=C_{n\mod p}^{m\mod p}\times Lucas(\frac{a}{p},\frac{b}{p},p) Lucas(n,m,p)=Cnmodpmmodp×Lucas(pa,pb,p)

代码
ll ksm(ll a,ll b,ll p) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = (ans * a) % p;
        a = (a * a) % p;
        b >>= 1;
    }
    return ans;
}

ll c(ll a,ll b) {
    if(a < b) return 0;
    if(a == b) return 1;
    if(b > a - b) b = a - b;
    ll A = 1,B = 1;
    for(ll i=0;i<b;i++) {
        A = (A * (a - i)) % p;
        B = (B * (b - i)) % p;
    }
    return (a * ksm(B,p - 2,p)) % p;
}

ll Lucas(ll n,ll m) {
    if(m == 0) return 1;
    return c(n % p,m % p) * Lucas(n / p,m / p) % p;
}
  • 32
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值