数论知识荟萃

有参考算法竞赛进阶指南。。

一、质数

  • 定义:质数(prime number)又称素数,有无限个。一个大于1的自然数,除了1和它本身外,不能被其他自然数整除,则称之为质数。

(1) 判定:

对于数(N),若之为合数,则存在一个数(T)能整除它其中,(2<=T<=sqrt{N})

复杂度:(O(sqrt{n}))

(2) 筛法:

对于数(N),我们要求出(1) ~ (N)的所有素数。

直接介绍线性筛法,对于数(m),我们考虑仅用它的最小质因子(q)筛去它,这是线筛的最基本思想。

#include <cstdio>
const int N=1000010;
int v[N],prime[N],cnt=0,n;
//v[i]代表i的最小质因子,prime[i]代表第i的素数是多少
void Prime()
{
    for(int i=2;i<=n;i++)
    {
        if(!v[i])
        {
            v[i]=i;
            prime[++cnt]=i;
        }
        for(int j=1;j<=cnt;j++)
        {
            int k=prime[j]*i;
            if(v[i]<prime[j]||k>n) continue;
            v[k]=prime[j];
        }
    }
}
int main()
{
    scanf("%d",&n);
    Prime();
    for(int i=1;i<=cnt;i++)
        printf("%d ",prime[i]);
    return 0;
}

复杂度:(O(n))

(3) 质因数分解

  • 算术基本定理:任何一个大于1的整数都能被分解成有限个质数的乘积

(N=p_1^{c^1}p_2^{c^2}...p_m^{c^m})(N=prod_{i=1}^m{p_i}^{c_i})

当不考虑顺序时,分解具有唯一性

这点在后面用的很多。

  • 质因数分解:
    和判定是否是质数有点像,我们用(1) ~ (sqrt{N})中的每一个数去试除(N)

复杂度:(O(sqrt{N}))

二、约数

(1) 整除,约数

整除的概念:一般的,设a,b为整数,且b≠0.如果存在整数q,使得a=bq,称b整除a,记作(b|a),也称b是a的约数。如果p不存在,称b不整除a,记作(b mid a).

整除的性质:

a|b,b|a (Rightarrow) a=b或a=-b

a|b,b|c (Rightarrow) a|c

a|b,a|c (Rightarrow) 对任意整数x,y,有a|bx+cy

(a|bc),且((a,b))=(1),则(a|c)

设p为素数,若(p|ab),则(p|a)(p|b)
设p为素数,若(p|a_1a_2...a_k),则存在(a_i) ((1≤i≤k)),使得(p|a_i).

设整数 (a) , (b) 不同时为0,则存在一对整数 (m) , (n) ,使得((a,b)=am+bn)(在一次同余方程和不定方程中经常用到)【裴蜀定理】

(2) 最大公约数与最小公倍数

  • 定义:(gcd(a,b))(或((a,b)))为最大公因数,(lcm[a,b])(或([a,b]))为最小公倍数

  • 欧几里得算法:((a,b)=(b,a) (mod) (b))

欧几里得就不证明了吧。

	int gcd(int a,int b)
    {
    	return b==0?a:gcd(b,a%b);
    }
  • ((a,b)[a,b]=|a||b|)

(3) 算术基本定理的推论:

(N=p_1^{c^1}p_2^{c^2}...p_m^{c^m})

  • (N)的正约数个数:
    (prod_{i=1}^m (c^i+1))

  • (N)的正约数之和:
    ((1+p_1+p_1^2+...+p_1^{c_1})*...*(1+p_m+p_m^2+...+p_m^{c_m})=prod_{i=1}^m(sum_{j=0}^{c_i} (p_i)^j))

(4) 互质

对整数(a,b),若(gcd(a,b)=1),则称(a)(b)互质。

(5) 欧拉函数

  • 定义:(1) ~ (N)中与(N)互质的数的个数,称为欧拉函数,记为(φ(N))
  1. 计算式
    (N)进行算术基本定理分解,即(N=p_1^{c^1}p_2^{c^2}...p_m^{c^m})(N=prod_{i=1}^m{p_i}^{c_i})
    (φ(N)=(1-1/p_1)*(1-1/p_2)*...*(1-p_m))(φ(N)=prod_{i=1}^m(1-1/p_i))
    感性证明(即并不是那么确切的,只是能加深记忆的证明·):
    对于(N)的某个质因子(p)(p*k)一定于(N)不互质((kin N^*,k*p<N)),即筛去后为(N-N/p)
    若此时引入第二个因子(q),同理筛去(N/q),但是多筛去了(q)(p)的公共倍数,加回来,即为
    (N-N/p-N/q+N/(q*p)=N(1-1/p)(1-1/q)),以此类推

计算式(O(sqrt N))求解(φ(N))

ll get(ll k)
{
    ll eu=k;
    for(ll i=2;i*i<=k;i++)
    {
        if(k%i==0)
            eu=eu*(i-1)/i;
        while(k%i==0)
            k/=i;
    }
    if(k>1) eu=eu*(k-1)/k;
    return eu;
}
  1. 性质
    ①若(gcd(a,b)=1),则(φ(ab)=φ(a)*φ(b))
    感性证明:带入欧拉函数定义式即可。
    【拓展】
    完全积性函数:是指所有对于任何(a,b)都有性质(f(ab)=f(a)f(b))的数论函数。
    积性函数:是指所有对于(gcd(a,b)=1)(a,b)都有性质(f(ab)=f(a)f(b))的数论函数。
    ②若质数(q)满足(q|n)(q^2|n),则(φ(q)=φ(n/q)*q)
    代入计算式可以得到
    ③若质数(q)满足(q|n)(q^2 mid n),则(φ(q)=φ(n/q)*(q-1))
    由积性函数性质得到
    (sum_{d|n}φ(d)=n)
    先证明是积性函数,再讨论单因子情况即可
    (φ(n)*n/2=sum_{d},gcd(d,n)=1)
    (gcd(x,n)=1),则(gcd(n-x,n)=1)。即它们是成对存在的。
    对于以上证明,其实自己多证几遍就记住了。

  2. 求解(φ(N))
    (N)分解质因数后,代入定义式计算。
    复杂度:(O(sqrt{N}))

  3. 求解(φ(1))~(φ(N))
    借助性质①②③和线性筛的思想。
    我们在线性筛筛去合数的同时推出这个合数的(φ(i))
    code:

int v[N],prime[N],phi[N],cnt=0,n;
void eular()
{
    for(int i=2;i<=n;i++)
    {
        if(!v[i])
        {
            v[i]=i;
            prime[++cnt]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=cnt;j++)
        {
            int tmp=i*prime[j];
            if(v[i]<prime[j]||tmp>n) break;
            v[tmp]=prime[j];
            phi[tmp]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);
        }
    }
}

三、同余

(1)同余的概念:一般的,设n为正整数,a和b为整数,如果a和b被n除后余数相同,那么称a和b模n同余,记作(a≡b) ((mod) (n))

(2)同余的性质:

  1. (a≡b) ((mod) (n)) (Leftrightarrow) (n|a-b)
  2. (a≡b) ((mod) (n)),且(c≡d) ((mod) (n))
    (a+c≡b+d) ((mod) (n))
    (ac≡bd) ((mod) (n))
  3. (ka≡kb) ((mod) (n)) (k为任意整数)
    (ab≡ac) ((mod) (n)),设(d=(a,n)),则(b≡c) ((mod) (n/d))
  4. (a^m≡b^m) ((mod) (n)) (m为正整数)

这里证明一下第三条第二点
(ab≡ac) ((mod) (n)) (d=(a,n))
(Rightarrow n | a(b-c))
(Rightarrow n/d |a(b-c)/d)
(gcd(n/d,a/d)=1)
(Rightarrow n/d |(b-c))
(Rightarrow b≡c(mod n/d))

(3)一次同余方程求解(exgcd或者叫大衍求一术):

对于一次同余方程(ax≡b(mod m))
(Rightarrow m|ax-b)
(-ym=ax-b)
即为(ax+my=b)
转化为求解一元二次不定方程

(ax+by=d)
(gcd(a,b)|d)时,有整数解

证明即求解方法:
由欧几里得:(gcd(a,b)=gcd(b,r)=...=gcd(a_n,0))
使用数学归纳法
对最后一步:(a_n*x_n+0*y_n=a_n)
显然对这个方程有解(x_n=1,y_n=0)
(a_m*x_m+b_m*y_m=d)有解
则对(a_{m-1}*x_{m-1}+b_{m-1}*y_{m-1}=d)
(b_{m-1}=a_m,a_{m-1}=lfloor a_{m-1}/a_m floor*a_m+b_m)(gcd得出)
(Rightarrow (lfloor a_{m-1}/a_m floor *a_m+b_m)*x_{m-1}+a_m*y_{m-1}=d)
(Rightarrow (k*x_{m-1}+y_{m-1})*a_m+x_{m-1}*b_m=d)
(Rightarrow x_{m-1}=y_m,y_{m-1}=x_m-lfloor a_{m-1}/a_m floor *y_m)
由以上,我们得到了它的证明和解法

(4)欧拉定理

(gcd(a,n)=1),则有(a^{φ(n)}≡1(mod) (n))

证明:
定义简化剩余类:把所有与整数a(a与n互质)模n同余的整数构成的集合叫做模n的一个简化剩余类,记作(a[n]),并把a叫做简化剩余类(a[n])的一个简化剩余系。

先证明当集合(x)恰好遍历(即取遍)(n)每一个简化剩余系时,(kx)也遍历。((k)为任意整数,(gcd(x,n)=1))

反证:若有(kx_i≡kx_j (mod) (n))
(x_i≡x_j (mod) (n))
矛盾,得证

下面欧拉定理:
({overline{a_1},overline{a_2},...,overline{a_φ(n)}})(n)的一个简化剩余类
由上({overline{a*a_1},overline{a*a_2},...,overline{a*a_φ(n)}})也是
(a*a_1*a*a_2...a*a_{φ(n)}≡a_1*a_2...a_{φ(n)} (mod) (n))
(a^{φ(n)}≡1(mod) (n))

特别的,当(n)取质数时,有(φ(n)=n-1)
(a^{n-1}≡1 (mod) (n)),即为费马小定理

(5)乘法逆元

对于"+"、"-"、"*",我们都已经得到了它在mod p下的转换式,那么对于"/"呢?

定义:若整数(b,m)互质,并且(b|a),则存在一个整数(x),使得(a/b≡a*x(mod) (m))。称(x)(b)的乘法逆元,记为(b^{-1}≡(mod) (m))
对于这个有解性可以转换成一次同余方程的有解来证明,不多赘述。

求法:(a/b≡a*b^{-1}≡a/b*b*b^{-1}(mod) (m)),即(b*b^{-1}≡1(mod) (m)),即转换为同余方程求解。特别的,当(m)为质数时,可以用费马小定理求解

  • (O(n))求解(1) ~ (n(mod) (p))的逆元
    假设(p=k*i+r,k<i),我们尝试用(k^{-1})(i^{-1})
    (k*i+p≡0(mod) (p))
    (Rightarrow k*i≡-r(mod) (p))
    (Rightarrow k*i*i^{-1}*r^{-1}≡-r*r^{-1}*i^{-1} (mod) (p))
    (Rightarrow -k*r^{-1}≡i^{-1} (mod) (p))
    (Rightarrow i^{-1}≡-lfloor p/i floor *(p) (mod) (i)^{-1} (mod) (p))

其实,这本质上是利用了逆元也是积性函数的性质。
参考代码:

#include <cstdio>
#define ll long long
const int N=3000010;
ll n,p,inv[N];
int main()
{
    scanf("%lld%lld",&n,&p);
    inv[1]=1;printf("1
");
    for(int i=2;i<=n;i++)
    {
        inv[i]=((inv[p%i]*(p-p/i))+p)%p;
        printf("%lld
",inv[i]);
    }
    return 0;
}
  • (O(n))求解(1!)~(n!)的逆元((p)为质数)
    设第(i)项阶乘为(f_i),则有(f_i=i*f_{i-1})
    若已经求得({f_i}^{-1})
    则有(i*{f_{i-1}}*{f_i}^{-1}≡f_{i-1}*{f_{i-1}}^{-1} (mod p))
    化简得 (f_{i-1}^{-1}≡i*{f_i}^{-1} (mod p))

参考代码:

void getinv()
{
    inv[0]=1;
    fac[0]=1;
    for(int i=1;i<=n;i++)
        fac[i]=(fac[i-1]*i)%mod;
    inv[n]=quick_pow(fac[n],n-2);
    for(int i=n-1;i;i--)
        inv[i]=(inv[i+1]*(i+1))%mod;
}

(6)中国剩余定理(孙子定理)

求解一次同余方程组,其中(m_1,m_2,...m_n)两两互质

[egin{equation} left{ egin{aligned} x ≡ b_1 (mod m_1) \ x ≡ b_2 (mod m_2) \ ... \ x ≡ b_n (mod m_n) \ end{aligned} ight. end{equation} ]

方法:先找到整数(L_1),使得(L_1≡1(mod m_1),b_1|L_1,b_2|L_1,...,b_n|L_1)
(L_1)是同余方程

[egin{equation} left{ egin{aligned} x ≡ 1 (mod m_1) \ x ≡ 0 (mod m_2) \ ... \ x ≡ 0 (mod m_n) \ end{aligned} ight. end{equation} ]

的解。

(L_1)乘上(b_1),即满足了第一个方程,而其余无影响。

然后找到(L_2,...,L_n).
(x=prod_{i=1}^n L_i)即为同余方程的解。

下面讨论如何得到(L_i)
(M=prod_{i=1}^n m_i),设(M_i=M/m_i)
(M_i)满足整除除了(b_i)的所有(b)

然后我们要求(L_i≡1(mod b_i))
(M_i*t_i≡1(mod b_i))
即转换为了求解一次同余方程的问题。

我们对同余方程组进行(n)次同余方程的求解即可。

解为
(x≡prod_{i=1}^n M_i*t_i*b_i (mod M))

参考代码:中国剩余定理

四、组合计数

(1)加法原理与乘法原理

加法原理:做一件事,完成它可以有(n)类办法,在第一类办法中有(m_1)种不同的方法,在第二类办法中有(m_2)种不同的方法,……,在第(n)类办法中有(m_n)种不同的方法,那么完成这件事共有(N=sum{i=1}^n m_i)种不同方法。每一种方法都能够直接达成目标。

乘法原理:做一件事,完成它需要分成(n)个步骤,做第一步有(m_1)种不同的方法,做第二步有(m_2)种不同的方法,……,做第(n)步有(m_n)种不同的方法,那么完成这件事共有(N=prod_{i=1}^n m_i)种不同的方法。

(2)排列组合

(n)个互异元素中依次取出(m)个元素存在顺序的排除一列,产生的不同排列的数量是
(A_n^m=n*(n-1)*(n-2)*...*(n-m+1)=frac{n!}{(n-m)!})

(m)个互异元素中取出m个元素组成一个集合不存在顺序,产生的不同组合的数量是
(C_n^m=frac{A_n^m}{m!}=frac{n!}{m!(n-m)!})

考虑取出(m)个元素会产生(m!)种顺序

  • 组合数的性质
    (C_n^m=C_n^{(n-m)})
    感性证明:从集合中取出了(m)个元素就剩下了(n-m)个元素,方案数是相等的
    (C_n^m=C_{n-1}^{m}+C_{n-1}^{m-1})(杨辉三角)
    这一点十分重要,由此可以得出组合数的递推式。
    感性证明:现在已经取到了第(n)个元素。我们有两个选择,取第(n)个元素或不取第(n)个元素
    若取第(n)个元素,则先在前(n-1)个元素中取(m-1)个,为(C_{n-1}^{m-1})
    若不取第(n)个元素,则先在前(n-1)个元素中取(m)个,为(C_{n-1}^m)
    根据加法原理,可以得到(C_n^m=C_{n-1}^{m}+C_{n-1}^{m-1})
    (sum{i=0}^n C_n^i=2^n)
    感性证明:从(n)个互异元素中取出若干个组成一个集合,有(n+1)种方法,分别取出0,1,..n个元素,又由加法原理,有(sum{i=1}^n)种方法
    从另一种角度看,每个元素在原集合里都有两种情况,取或不取,方案数为(2^n)

以上也可以代入计算式进行验证。

  • 组合数的求法
    ①用第二个性质,可以在(O(n^2))求出0<=j<=i<=n的所有组合数(C_i^j)
void get()
{
    C[1][1]=1;
    for(int i=2;i<=N;i++)
        for(int j=1;j<=i;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}

②用计算式在(O(nlogn))求出某一个组合数(C_i^j)
因为结果一般较大,通常会用mod取一下模
所以先求出分母(m!(n-m)!)的逆元

void getinv()
{
    inv[0]=1;
    fac[0]=1;
    for(int i=1;i<=n;i++)
        fac[i]=(fac[i-1]*i)%mod;
    inv[n]=quick_pow(fac[n],n-2);
    for(int i=n-1;i;i--)
        inv[i]=(inv[i+1]*(i+1))%mod;
}
int get(int i,int j)
{
    return (fac[i]*inv[j]*inv[i-j])%mod;
}

(3) 二项式定理

((x+y)^n=sum_{i=0}^n C_n^i*a^i*b^{n-i})
这个其实手玩一下就出来了,用排列组合的思想考虑每一项出现的次数。
下面证明(Lucas)定理要用到

(4) (Lucas) 定理

(p)是素数,则有
(C_n^m≡C_{n\%p}^{m\%p}*C_{n/p}^{m/p} (mod p))

证明:
首先素数的条件用在这个地方
(p)为质数时,任取(x in (1,p)且x in N),满足(C_p^x≡0 (mod p))。这点不难,不证明了。

(n=ap+r_1,m=bp+r_2)

((1+x)^n%p)这样一个式子,我们对它进行变形(本质上对应将数进行p进制分解)

((1+x)^n=(1+x)^{ap+r_1}=((1+x)^p)^a*(1+x)^{r_1})
(≡(1+x^p)^a*(1+x)^{r_1}=sum_{i=0}^a C_a^i*x^{i*p}*sum C_{r_1=0}^j*x^j (mod p))(注意最后两步用了二项式定理化简,第三步用了那个没证明的玩意儿)

整理得((1+x)^n≡sum_{i=0}^a C_a^i*x^{i*p}*sum C_{r_1=0}^j*x^j (mod p))

对左边这样一项(x^{bp+r_2})来说,它的系数为(C_{ap+r_1}^{bq+r_2})

而右边当且仅当(i=b,j=r_2)时,可以构成这个项,而它的系数为(C_a^b*C_{r_1}^{r_2})

(C_n^m≡C_{n\%p}^{m\%p}*C_{n/p}^{m/p} (mod p)),则得证

咕咕咕


开始更新组合数学 2018.6.29

原文地址:https://www.cnblogs.com/butterflydew/p/9108517.html