数论笔记

1. 质数

1.1 质数的判定

试除法

若一个正整数 N N N为合数(除了能被1和自身以外的数整除),则存在一个能整除 N N N的数 T T T,其中 2 ≤ T ≤ N 2\leq T\leq \sqrt N 2TN

bool is_prime(int n)
{
    if (n < 2) return false;
    for (int i = 2; i < sqrt(n); ++ i )
    	if (n % i == 0) return false;
   	return true;
}

1.2 质数的筛选

Eratosthenes筛法

基本思想:任意整数 x x x的倍数 2 x , 3 x , . . . [ N / x ] ∗ x 2x, 3x, ...[N/x]*x 2x,3x,...[N/x]x都不是质数。

void primes(int n)
{
    memset(v, 0, sizeof(v)); //合数标记
    for (int i = 2; i <= n; ++ i )
    {
        if (v[i]) continue;
        cout << i << endl; // i是质数
        for (int j = i; j <= n/i; ++ j ) v[i*j] = 1;
    }
}

Eratosthenes筛法的时间复杂度为 O ( N l o g l o g N ) O(NloglogN) O(NloglogN)。该算法效率非常接近线性。

线性筛法

线性筛法通过“从大到小累计质因子”的方式标记合数,即让12只有 3 ∗ 2 ∗ 2 3*2*2 322一种产生方式。

int primes[N], cnt = 0;     // primes[]存储所有素数
bool st[N];         // st[x]存储x是否被筛掉

void get_primes(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i]) primes[cnt ++ ] = i;
        for (int j = 0; primes[j] <= n/i; j ++ )
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

1.3 质因数分解

定理:任何一个大于1的正整数都能唯一分解为有限个质数的乘积,可写作:
N = p 1 c 1 p 2 c 2 . . . p m c m N = p^{c_1}_1p^{c_2}_2...p^{c_m}_m N=p1c1p2c2...pmcm
其中 c i c_i ci都是正整数, p i p_i pi都是质数,且满足 p 1 < p 2 < . . . < p m p_1p1<p2<...<pm

试除法

void divide(int n)
{
    m = 0;
    for (int i = 2; i <= sqrt(n); ++ i )
    {
        if (n % i == 0)
        {
            p[++m] = i, c[m] = 0;
            while (n % i == 0) n /= i, c[m]++;
        }
    }
    
    if (n > 1) // n是质数
        p[++m] = n, c[m] = 1;
    for (int i = 1; i <= m; ++ i )
        cout << p[i] << " " << c[i] << endl;
}

2.约数

定义:若整数 n n n除以整数 d d d的余数为0,即 d d d能整出 n n n,则称 d d d n n n的约数,记为 d ∣ n d|n dn

关于约数的一个定理:一个数有奇数个约数,那么这个数是平方数

2.1 算术基本定理的推论

在算术基本定理中,若正整数 N N N被唯一分解为 N = p 1 c 1 p 2 c 2 . . . p m c m N = p^{c_1}_1p^{c_2}_2...p^{c_m}_m N=p1c1p2c2...pmcm,其中 c i c_i ci都是正整数, p i p_i pi都是质数,且满足 p 1 < p 2 < . . . < p m p_1p1<p2<...<pm,则 N N N的正约数集合可以写作:
{ p 1 c 1 p 2 c 2 . . . p m c m } , 其 中 0 ≤ b i ≤ c i \{p^{c_1}_1p^{c_2}_2...p^{c_m}_m\},其中0\leq b_i\leq c_i {p1c1p2c2...pmcm},0bici
N N N的正约数个数为:
( c 1 + 1 ) ∗ ( c 2 + 1 ) ∗ . . . ( c m + 1 ) = ∏ i = 1 m ( c i + 1 ) (c_1+1)*(c_2+1)*...(c_m+1)=\prod^{m}_{i=1}(c_i+1) (c1+1)(c2+1)...(cm+1)=i=1m(ci+1)
N N N的所有正约数的和为:
( 1 + p 1 + p 1 2 + . . . + p 1 c 1 ) ∗ . . . ∗ ( 1 + p m + p m 2 + . . . + p m c m ) = ∏ i = 1 m ( ∑ j = 0 c i ( p i ) j ) (1+p_1+p_1^2+...+p_1^{c_1})*...*(1+p_m+p_m^2+...+p_m^{c_m}) = \prod^m_{i=1}(\sum^{c_i}_{j=0}(p_i)^j) (1+p1+p12+...+p1c1)...(1+pm+pm2+...+pmcm)=i=1m(j=0ci(pi)j)

2.2 求 N N N的正约数集合—试除法

d ≥ N d\geq\sqrt N dN N N N的约数,则 N / d ≤ N N/d\leq \sqrt N N/dN 也是 N N N的约数。换言之,约数是成对出现的(除了完全平方数)。

int factor[1600], m = 0;

for (int i = 2; i*i <= n; ++ i )
{
    if (n % i == 0)
    {
        factor[++m] = i;
        if (n/i != i) factor[++m] = n/i;
    }
}

for (int i = 1; i <= m; ++ i )
    cout << factor[i] << endl;

试除法的推论

一个整数 N N N的约数个数上界为 2 N 2\sqrt N 2N

2.3 求 1 ∼ N 1 \sim N 1N每个数的正约数集合—倍数法

对于每个数 d d d, 1 ∼ N 1\sim N 1N中以 d d d为约数的数就是 d d d的倍数 d , 2 d , . . . [ n / d ] ∗ d d,2d,...[n/d]*d d,2d,...[n/d]d

vector<int> factor[500010];

for (int i = 1; i <= n; ++ i )
{
    for (int j = 1; j <= n/i; ++ j )
        factor[i*j].push_back(i);
}

for (int i = 1; i <= n; ++ i )
{
	for (auto &x : factor[i])
    {
        cout << x << " ";
    }
    puts("");
}

上述算法的时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)

倍数法的推论

1 ∼ N 1\sim N 1N每个数的约数个数的总和大约为 N l o g N NlogN NlogN

2.4 最大公约数

定理: ∀ a , b ⊂ N , g c d ( a , b ) ∗ l c m ( a , b ) = a ∗ b \forall a,b\subset \N, gcd(a,b) *lcm(a,b)=a*b a,bN,gcd(a,b)lcm(a,b)=ab

欧几里得算法

int gcd(int a, int b)
{
    return b ? gcd(b, a%b) : a;
}

2.5 互质与欧拉函数

定义: ∀ a , b ⊂ N , g c d ( a , b ) = 1 \forall a,b\subset \N, gcd(a,b) =1 a,bN,gcd(a,b)=1,则称a,b互质。

欧拉函数

1 ∼ N 1\sim N 1N中与 N N N互质的数的个数被称为欧拉函数,记为 φ ( N ) \varphi (N) φ(N)

若在算术基本定理中, N = p 1 c 1 p 2 c 2 . . . p m c m N = p^{c_1}_1p^{c_2}_2...p^{c_m}_m N=p1c1p2c2...pmcm,则
φ ( N ) = N ∗ p 1 − 1 p 1 ∗ p 2 − 1 p 2 ∗ . . . ∗ p m − 1 p m = N ∗ ∏ 质 数 p ∣ N ( 1 − 1 p ) \varphi (N) = N*\frac{p_1-1}{p_1}*\frac{p_2-1}{p_2}*...*\frac{p_m-1}{p_m}=N*\prod_{质数p|N}(1-\frac{1}{p}) φ(N)=Np1p11p2p21...pmpm1=NpN(1p1)

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

欧拉函数的常用性质:

  1. 如果 n , m n,m n,m互质,则 φ ( n m ) = φ ( n ) ∗ φ ( m ) \varphi(nm) = \varphi(n)*\varphi(m) φ(nm)=φ(n)φ(m)
  2. ∀ n > 1 , 1 ∼ n \forall n>1,1 \sim n n>1,1n中与 n n n互质的数的和为 n ∗ φ ( n ) / 2 n*\varphi(n)/2 nφ(n)/2
  3. p p p为质数,若 p ∣ n p|n pn p 2 ∣ n p^2|n p2n,则 φ ( n ) = φ ( n / p ) ∗ p \varphi(n)=\varphi(n/p)*p φ(n)=φ(n/p)p
  4. p p p为质数,若 p ∣ n p|n pn p 2 ∤ n p^2\nmid n p2n,则 φ ( n ) = φ ( n / p ) ∗ ( p − 1 ) \varphi(n)=\varphi(n/p)*(p-1) φ(n)=φ(n/p)(p1)
  5. 欧拉定理:如果 a , n a,n a,n互质,且均为正整数,则 a φ ( n ) ≡ 1 ( m o d   n ) a^{\varphi(n)} \equiv 1(mod \ n) aφ(n)1(mod n)

筛法求欧拉函数

int primes[N], euler[N], cnt;
bool st[N];

// 质数存在primes[]中,euler[i] 表示
// i的欧拉函数
void get_eulers(int n)
{
    euler[1] = 1;
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i])
        {
            primes[cnt ++ ] = i;
            euler[i] = i - 1;
        }
        for (int j = 0; primes[j] <= n/i; j ++ )
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0)
            {
                euler[i * primes[j]] = euler[i] * primes[j];
                break;
            }
            euler[i * primes[j]] = euler[i] * (primes[j] - 1);
        }
    }
}

同余

定义:若整数 a a a b b b除以正整数 m m m的余数相等,则称 a , b a,b a,b模m同余,记为 a ≡ b ( m o d   m ) a\equiv b(mod\ m) ab(mod m)

费马小定理

​ 若 p p p是质数,而整数 a a a不是 p p p的倍数,有 a p ≡ a ( m o d   p ) a^p\equiv a(mod\ p) apa(mod p)

欧拉定理:

​ 若正整数 a , n a,n a,n互质,则 a φ ( n ) ≡ 1 ( m o d   n ) a^{\varphi(n)} \equiv 1(mod \ n) aφ(n)1(mod n),其中 φ ( n ) \varphi(n) φ(n)为欧拉函数。

欧拉定理的推论

​ 若正整数 a , b a,b a,b互质,则对于任意正整数 b b b a b ≡ a b   m o d   φ ( n ) ( m o d   n ) a^b \equiv a^{b\ mod\ \varphi(n)}(mod \ n) abab mod φ(n)(mod n)

扩展欧几里得算法

裴蜀定理

​ 对于任意整数 a , b a,b a,b,存在一对整数 x , y x,y x,y,满足 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)

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

乘法逆元

​ 若整数 b , m b,m b,m互质,并且 b ∣ a b|a ba,则存在一个整数 x x x,使得 a / b ≡ a ∗ x ( m o d   m ) a/b\equiv a*x(mod\ m) a/bax(mod m)。称 x x x b b b的模 m m m乘法逆元,记为 b − 1 ( m o d   m ) b^{-1}(mod\ m) b1(mod m)

​ 因为 a / b ≡ a ∗ b − 1 ≡ a / b ∗ b ∗ b − 1 ( m o d   m ) a/b\equiv a*b^{-1}\equiv a/b*b*b^{-1}(mod\ m) a/bab1a/bbb1(mod m),所以 b ∗ b − 1 ≡ 1 ( m o d   m ) b*b^{-1}\equiv 1(mod\ m) bb11(mod m)

​ 当模数m为质数时, b m − 2 b^{m-2} bm2即为 b b b的乘法逆元。如果只是保证 b , m b,m b,m互质,那么乘法逆元可通过解同余方程 b ∗ x ≡ 1 ( m o d   m ) b*x\equiv 1(mod\ m) bx1(mod m)得到。

线性同余方程

给定整数 a , b , m a,b,m a,b,m,求一个整数 x x x满足 a ∗ x ≡ b ( m o d   m ) a*x\equiv b(mod\ m) axb(mod m),或者给出无解。因为未知数的指数为1,所以我们称之为一次同余方程,也称为线性同余方程

a ∗ x ≡ b ( m o d   m ) a*x\equiv b(mod\ m) axb(mod m)等价于 a ∗ x − b a*x-b axb m m m的倍数,不妨设为 − y -y y倍。于是,该方程可以改写为 a ∗ x + m ∗ y = b a*x+m*y=b ax+my=b

根据前面裴蜀定理的证明,线性同余方程有解当且仅当 g c d ( a , m ) ∣ b gcd(a,m)|b gcd(a,m)b

中国剩余定理

​ 设 m 1 , m 2 , . . . , m n m_1,m_2,...,m_n m1,m2,...,mn是两两互质的整数, m = ∏ i = 1 n m i , M i = m / m i , t i m=\prod^n_{i=1}m_i,M_i=m/m_i,t_i m=i=1nmi,Mi=m/mi,ti是线性同余方程 M i t i ≡ 1 ( m o d   m i ) M_it_i\equiv 1(mod\ m_i) Miti1(mod mi)的一个解。对于任意的 n n n个整数 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,方程组
x ≡ a 1 ( m o d   m 1 ) , x ≡ a 2 ( m o d   m 2 ) , . . . , x ≡ a n ( m o d   m n ) x\equiv a_1(mod\ m_1),x\equiv a_2(mod\ m_2),...,x\equiv a_n(mod\ m_n) xa1(mod m1),xa2(mod m2),...,xan(mod mn)
​ 有整数解,解为 x = ∑ i = 1 n a i M i t i x=\sum^n_{i=1}a_iM_it_i x=i=1naiMiti

组合计数

排列数

​ 从 n n n个不同元素中依次取出 m m m个元素排成一列,产生的不同排列的数量为:
A n m = n ! ( n − m ) ! A^m_n=\frac{n!}{(n-m)!} Anm=(nm)!n!
组合数

​ 从n个不同元素中取出m个组成一个集合(不考虑顺序),产生的不同集合数量为:
C n m = n ! m ! ( n − m ) ! C^m_n=\frac{n!}{m!(n-m)!} Cnm=m!(nm)!n!
性质:

  1. C n m = c n n − m C_n^m=c_n^{n-m} Cnm=cnnm

  2. 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

  3. 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

    递归法求组合数

    // c[a][b] 表示从a个中选b个的方案数
    for (int i = 0; i < N; i ++ )
    {
        for (int j = 0; j <= i; j ++ )
        {
         	if (!j) c[i][j] = 1;
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;   
        }
    }
    

    通过预处理逆元的方式求组合数

    // 首先预处理出所有阶乘取模的余数fact[N],以及所有阶乘取模的逆元infact[N]
    // 如果取模的数是质数,可以用费马小定理求逆元
    int qmi(int a, int k, int p)    // 快速幂模板
    {
        int res = 1;
        while (k)
        {
            if (k & 1) res = (LL)res * a % p;
            a = (LL)a * a % p;
            k >>= 1;
        }
        return res;
    }
    
    // 预处理阶乘的余数和阶乘逆元的余数
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i ++ )
    {
        fact[i] = (LL)fact[i - 1] * i % mod;
        infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }
    

    多重集的排列数:

    ​ 多重集是指包含重复元素的广义集合。设 S = { n 1 ∗ a 1 , n 2 ∗ a 2 , . . . , a k ∗ a k } S=\{n_1*a_1,n_2*a_2,...,a_k*a_k \} S={n1a1,n2a2,...,akak}是由 n 1 n_1 n1 a 1 a_1 a1 n 2 n_2 n2 a 2 . . . n k a_2...n_k a2...nk a k a_k ak组成的多重集。 S S S的全排列个数为 n ! n 1 ! n 2 ! . . . n k ! \frac{n!}{n_1!n_2!...n_k!} n1!n2!...nk!n!

    多重集的组合数:

    ​ 从 S S S中取出 r ( r ≤ n i ) r(r\leq n_i) r(rni)个元素组成一个多重集(不考虑元素的顺序),产生的不同多重集的数量为 C k + r − 1 k − 1 C_{k+r-1}^{k-1} Ck+r1k1

    Lucas定理

你可能感兴趣的:(数据结构)