[数学基础] 2 组合数

本文详细介绍了组合数的计算方法,包括递推法、预处理逆元、分解质因数法、Lucas定理以及取对数法,并提供了对应的代码实现。此外,还总结了组合数的常用公式,如二项式定理和乘法原理等。最后,讨论了Lucas定理及其推论在组合数模p计算中的应用。
摘要由CSDN通过智能技术生成

组合数

1. 求组合数

根据不同的数据范围,求组合数也可以运用不同的方法。由于这是中学的内容,所以这里就不详细介绍了。
求解的总的式子:

C a b = a ! b ! ( a − b ) ! C_a^b=\frac{a!}{b!(a-b)!} Cab=b!(ab)!a!

表示从 a a a个物品中选出 b b b个的方案数。

(1) 递推法

使用递推式 C a b = C a − 1 b + C a − 1 b − 1 C_a^b=C_{a-1}^b+C_{a-1}^{b-1} Cab=Ca1b+Ca1b1

证明:考虑已经得知了 C a − 1 k , k ∈ [ 0 , b ] C_{a-1}^k,k\in[0,b] Ca1k,k[0,b]的结果,那么当前有 a a a个物品时,第 a a a个物品要么被选,要么不被选中。若被选中,则方案一共有 C a − 1 b − 1 C_{a-1}^{b-1} Ca1b1个,若不被选中,则方案有 C a − 1 b C_{a-1}^b Ca1b个,方案累加,得证。

时间复杂度 O ( n 2 ) O(n^2) O(n2)

void init(){
    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-1] + C[i-1][j]) % M;
            }
        }
    }
}
(2) 通过预处理逆元的方式求组合数

直接求出 a ! , 1 b ! , 1 ( a − b ) ! a!,\frac{1}{b!},\frac{1}{(a-b)!} a!,b!1,(ab)!1,然后再相乘。

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

int n;
const int N = 1e5 + 5;
const ll M = 1e9 + 7;
ll fac[N], invf[N];// 阶乘 阶乘的逆元

void init(){
    fac[0] = invf[0] = 1LL;
    for (int i=1;i<N;++i){
        fac[i] = fac[i-1] * i % M;
        invf[i] = invf[i-1] * qmi(i, M-2, M) % M;
    }
}

int main(void){
    scanf("%d", &n);
    init();
    ll a, b;
    for (int i=1;i<=n;++i){
        scanf("%lld%lld", &a, &b);
        ll ans = fac[a] * invf[b] % M * invf[a - b] % M;
        printf("%lld\n", ans);
    }
    
    return 0;
}
(3) 分解质因数法求组合数(高精度)

首先,可以把 a ! a! a!写成 a ! = p 1 a 1 × p 2 a 2 × . . . × p k a k a!=p_1^{a_1}\times p_2^{a_2}\times ...\times p_k^{a_k} a!=p1a1×p2a2×...×pkak的形式,那么 C a b C_a^b Cab的答案肯定可以写成 C a b = p 1 b 1 × p 2 b 2 × . . . × p k b k , ∀ i ∈ [ 1 , k ] , b i ≤ a i C_a^b=p_1^{b_1}\times p_2^{b_2}\times ...\times p_k^{b_k},\forall i\in[1,k],b_i\leq a_i Cab=p1b1×p2b2×...×pkbk,i[1,k],biai

因此,设 s i s_i si a ! a! a!中包含的质因子 p i p_i pi的个数减去 b ! , ( a − b ) ! b!,(a-b)! b!,(ab)!中包含的 p i p_i pi的个数,则 C a b = ∏ i = 1 k p i s i C_a^b=\prod_{i=1}^{k} p_i^{s_i} Cab=i=1kpisi

那么, a ! a! a!包含的质因子个数为多少呢?答案是 ⌊ a p ⌋ + ⌊ a p 2 ⌋ + ⌊ a p 3 ⌋ . . . \lfloor\frac{a}{p}\rfloor+\lfloor\frac{a}{p^2}\rfloor+\lfloor\frac{a}{p^3}\rfloor... pa+p2a+p3a...

  • 代码:
const int N = 5000 + 5;
int p[N], sa[N], cnt;
bool st[N];

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

int get(int n, int p){
	int res = 0;
	while (n){
		res += n/p;
		n/=p;
	}
	return res;
}

vector<int> mul(vector<int>& A, int b){
    vector<int> res; int t = 0;
    for (int i=0;i<A.size();++i){
        t += A[i]*b;
        res.push_back(t % 10), t /= 10;
    }
    while (t){ res.push_back(t%10), t /= 10;}
    while (res.back()==0 && res.size()>1) res.pop_back();
    return res;
}

int main(void){
	int a, b;
	scanf("%d%d", &a, &b);
	Euler(a);
	for (int i=1;i<=cnt;++i){
		int cur = p[i];
		sa[i] = get(a, cur) - get(a-b, cur) - get(b, cur);
	}
	
	vector<int> res;
	res.push_back(1);
	for (int i=1;i<=cnt;++i){
		for (int j=0;j<sa[i];++j){
			res = mul(res, p[i]);
		}
	}

	for (int i=res.size()-1;i>=0;--i){
		printf("%d", res[i]);
	}
	puts("");
	
	return 0;
}
(4) Lucas定理求组合数
  • Lucas定理 C a b ≡ C a m o d    p b m o d    p × C a / p b / p ( m o d    p ) C_a^b \equiv C_{a\mod p}^{b \mod p}\times C_{a/p}^{b/p} (\mod p) CabCamodpbmodp×Ca/pb/p(modp)

  • 代码

其中,组合数直接通过公式 C a b = a ! b ! ( a − b ) ! = a × ( a − 1 ) × . . . × ( a − b + 1 ) b ! C_a^b=\frac{a!}{b!(a-b)!}=\frac{a\times (a-1)\times...\times (a-b+1)}{b!} Cab=b!(ab)!a!=b!a×(a1)×...×(ab+1)求得

ll C(ll a, ll b, ll p){
	if (b > a) return 0;
	ll res = 1;
	for (int i=1, j=a;i<=b;++i, --j){
		res = res * j % p; // 分子
		res = res * qmi(i, p-2, p) % p; // 分母
	}
	return res;
}

ll lucas(ll a, ll b, ll p){
	if (a<p && b<p) return C(a, b, p);
	return C(a%p, b%p, p) * lucas(a/p, b/p, p) % p;
}
(5) 取对数求组合数(可能有浮点误差)

此种方法大概可以计算1000以内的组合数。

先假设 m ≤ n 2 m\leq \frac{n}{2} m2n吧,否则因为 C ( n , m ) = C ( n , m − n ) C(n,m)=C(n,m-n) C(n,m)=C(n,mn),令 m = m − n m=m-n m=mn

x = ln ⁡ C ( n , m ) = ln ⁡ ( n ! m ! ( n − m ) ! ) = ∑ i = 1 n ln ⁡ i − ∑ i = 1 m ln ⁡ i − ∑ i = 1 n − m ln ⁡ i x=\ln C(n,m)=\ln (\frac{n!}{m!(n-m)!})=\sum\limits _{i=1}^n\ln i-\sum\limits _{i=1}^m\ln i-\sum\limits _{i=1}^{n-m}\ln i x=lnC(n,m)=ln(m!(nm)!n!)=i=1nlnii=1mlnii=1nmlni

这样就可以直接计算前缀和 ln ⁡ n \ln n lnn,则 C ( n , m ) = e x C(n,m)=e^x C(n,m)=ex

2. 常用公式
(1) 二项式定理

Cannot read property 'type' of undefined

也可写成Cannot read property 'type' of undefined

其中,Cannot read property 'type' of undefined

二项式定理的一个常用形式为 ( 1 + x ) n = ∑ k = 0 n C n k x k (1+x)^n=\sum\limits_{k=0}^n C_n^kx^k (1+x)n=k=0nCnkxk

那么很明显, ( 1 − x ) n = ∑ k = 0 n ( − 1 ) k C n k x k (1-x)^n=\sum\limits_{k=0}^n (-1)^kC_n^kx^k (1x)n=k=0n(1)kCnkxk

(2) 其他的一些小公式

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(从 n n n取任意个数,显然二进制取或不取)

C a b = C a − 1 b + C a − 1 b − 1 C_a^b=C_{a-1}^b+C_{a-1}^{b-1} Cab=Ca1b+Ca1b1(类似DP思想,对于第 b b b个数,取或不取)

C m + n n = C m 0 C n n + C m 1 C n n − 1 + . . . + C m n C n 0 C_{m+n}^n=C_m^0C_n^n+C_m^1C_n^{n-1}+...+C_m^nC_n^0 Cm+nn=Cm0Cnn+Cm1Cnn1+...+CmnCn0(从 m m m个数取 n n n个, n n n个取0个开始遍历)

C n m = C n n − m C_n^m=C_n^{n-m} Cnm=Cnnm(显然从 n n n中取 m m m个的方案数和从 n n n中取 n − m n-m nm个的方案数是一样的)

(这些递推式还是挺容易证明的就不写了,敲公式好累)

(2) 乘法原理和加法原理:类类独立,步步相关
  1. 加法原理:做一件事情,完成它可以有 n n n类办法,在第一类办法中有 m 1 m_1 m1种不同的方法,在第二类办法中有 m 2 m_2 m2种不同的方法,……,在第 n n n类办法中有 m n m_n mn种不同的方法。那么完成这件事共有 N = ∑ i = 1 n m i N=\sum_{i=1}^n m_i N=i=1nmi种不同的方法。

  2. 乘法原理:做一件事情,完成它需要分成n个步骤,做第一步有 m 1 m_1 m1种不同的方法,做第二步有m2种不同的方法,……,做第 n n n步有种 m n m_n mn不同的方法,那么完成这件事有 N = ∏ i = 1 n m i N=\prod_{i=1}^n m_i Ni=1nmi种不同的方法。

(3) 可重复元素的组合问题

从n个元素中,可重复的挑选m个元素组成集合,求:不同的集合有多少个?

答案: C n + m − 1 m C_{n+m-1}^m Cn+m1m

证明:隔板法。

∣∗∣∗∗∗∗∣∣∗∗∗∣∣∗∣(|是边界,*是球)

假设这里有n个盒子,m个球,如果有k个球被放在了第i个盒子里,那么就相当于第i个数被选了k次。那么去掉两端边界,就相当于摆放n-1个盒子的边界和m个球有多少种摆放方式。从里面选择r个位置放球 剩余的都是盒子边界,因此答案就是 C n + m − 1 m C_{n+m-1}^m Cn+m1m

这里有位老哥用dp在 O ( n 2 ) O(n^2) O(n2)搞定了,俺觉得也可以一看wwww,部分dp表真是一个好东西啊。

https://blog.csdn.net/m0_37602827/article/details/100624871

3. 常用定理
(1) Lucas定理

结论 C a b ≡ C a m o d    p b m o d    p × C a / p b / p ( m o d    p ) C_a^b \equiv C_{a\mod p}^{b \mod p}\times C_{a/p}^{b/p} (\mod p) CabCamodpbmodp×Ca/pb/p(modp) p p p为素数)

它还可以写成如下形式:

p p p为素数,则 n n n p p p进制表示为 ( a k , a k − 1 , . . . , a 0 ) (a_k,a_{k-1},...,a_0) (ak,ak1,...,a0) m m m p p p进制表示为 ( b k , b k − 1 , . . . , b 0 ) (b_k,b_{k-1},...,b_0) (bk,bk1,...,b0)

C n m ≡ C a k b k ⋅ C a k − 1 b k − 1 . . . C a 0 b 0 ( m o d    p ) C_n^m\equiv C_{a_k}^{b_k}·C_{a_{k-1}}^{b_{k-1}}...C_{a_0}^{b_0} (\mod p) CnmCakbkCak1bk1...Ca0b0(modp)

很容易看出来,它就是形式一运用数学归纳法可以得出的结论。

证明

引理1 C ( p , x ) ≡ 0 ( m o d    p ) , 0 < x < p C(p,x) \equiv 0(\mod p), 0<x<p C(p,x)0(modp),0<x<p

证明:$C(p,x)\equiv \frac{p!}{x!(p-x)!}\equiv \frac{p\times (p-1)!}{x\times(x-1)\times(p-x)!} $

由于 p p p为素数,所以 ∀ x ∈ ( 0 , p ) , ( p , x ) = 1 \forall x\in(0,p),(p,x)=1 x(0,p),(p,x)=1,故 x ∣ C p − 1 x − 1 x|C_{p-1}^{x-1} xCp1x1,则 p ∣ ( p x C p − 1 x − 1 ) p|(\frac{p}{x}C_{p-1}^{x-1}) p(xpCp1x1)

所以 C ( p , x ) ≡ p × ( x − 1 m o d    p ) × C ( p − 1 , x − 1 ) ≡ 0 m o d    p C(p,x) \equiv p\times(x^{-1} \mod p)\times C(p-1,x-1)\equiv 0\mod p C(p,x)p×(x1modp)×C(p1,x1)0modp

引理2 ( 1 + x ) p ≡ ( 1 + x p ) m o d    p (1+x)^p \equiv (1+x^p) \mod p (1+x)p(1+xp)modp

根据二项式定理可得 ( 1 + x ) p = ∑ k = 0 p C p k x k (1+x)^p=\sum\limits _{k=0}^pC_p^kx^k (1+x)p=k=0pCpkxk

由引理1可得 ∑ k = 0 p C p k x k ≡ C ( p , 0 ) x 0 + C ( p , p ) x p ≡ ( 1 + x p ) m o d    p \sum\limits _{k=0}^pC_p^kx^k\equiv C(p,0)x^0+C(p,p)x^p\equiv (1+x^p) \mod p k=0pCpkxkC(p,0)x0+C(p,p)xp(1+xp)modp

下面正式推导Lucas定理。

假设 n = q 1 p + r 1 , m = q 2 p + r 2 n=q_1p+r_1,m=q_2p+r_2 n=q1p+r1,m=q2p+r2,则 q 1 = n / p , q 2 = m / p q_1=n/p,q_2=m/p q1=n/p,q2=m/p

( 1 + x ) n ≡ ( 1 + x ) q 1 p + r 1 ≡ ( 1 + x ) q 1 p × ( 1 + x ) r 1 ≡ [ ( 1 + x ) p ] q 1 × ( 1 + x ) r 1 ( 1 + x p ) q 1 × ( 1 + x ) r 1 ≡ ∑ i = 0 q 1 C ( q 1 , i ) x p × i × ∑ j = 0 r 1 C ( r 1 , j ) x j ( m o d    p ) (1+x)^n\equiv (1+x)^{q_1p+r_1}\equiv (1+x)^{q_1p}\times(1+x)^{r_1}\equiv [(1+x)^p]^{q_1}\times(1+x)^{r_1} \\ (1+x^p)^{q_1}\times(1+x)^{r_1} \equiv \sum\limits _{i=0}^{q_1}C(q_1,i)x^{p\times i}\times \sum\limits _{j=0}^{r_1}C(r_1,j)x^{j} (\mod p) (1+x)n(1+x)q1p+r1(1+x)q1p×(1+x)r1[(1+x)p]q1×(1+x)r1(1+xp)q1×(1+x)r1i=0q1C(q1,i)xp×i×j=0r1C(r1,j)xj(modp)

( 1 + x ) n = ∑ i = 0 n C ( n , i ) x i (1+x)^n=\sum\limits _{i=0}^{n}C(n,i)x^{i} (1+x)n=i=0nC(n,i)xi

∴ ∑ i = 0 n C ( n , i ) x i ≡ ∑ i = 0 q 1 C ( q 1 , i ) x p × i × ∑ j = 0 r 1 C ( r 1 , j ) x j ( m o d    p ) \therefore \sum\limits _{i=0}^{n}C(n,i)x^{i}\equiv \sum\limits _{i=0}^{q_1}C(q_1,i)x^{p\times i}\times \sum\limits _{j=0}^{r_1}C(r_1,j)x^{j} (\mod p) i=0nC(n,i)xii=0q1C(q1,i)xp×i×j=0r1C(r1,j)xj(modp)

由二项式定理可知, C n m C_n^m Cnm ( 1 + x ) n (1+x)^n (1+x)n展开式中 x m x^m xm项前面的系数

∵ m = q 2 p + r 2 , ∴ q 2 = m / p , r 2 = m − ⌊ m p ⌋ × p \because m=q_2p+r_2,\therefore q_2=m/p,r_2=m-\lfloor \frac{m}{p} \rfloor \times p m=q2p+r2,q2=m/p,r2=mpm×p

C ( n , m ) x m ≡ C ( q 1 , q 2 ) x p × q 2 × C ( r 1 , r 2 ) x r 2 ( m o d    p ) C(n,m)x^{m}\equiv C(q_1,q_2)x^{p\times q_2}\times C(r_1,r_2)x^{r_2} (\mod p) C(n,m)xmC(q1,q2)xp×q2×C(r1,r2)xr2(modp)

C n m ≡ C q 1 q 2 ⋅ C r 1 r 2 ( m o d    p ) C_n^m\equiv C_{q_1}^{q_2}·C_{r_1}^{r_2} (\mod p) CnmCq1q2Cr1r2(modp),得证。

推论1 C ( n , m ) C(n,m) C(n,m)为奇数的充要条件为,在二进制表示下( p = 2 p=2 p=2), ∀ i ∈ [ 0 , k ] , a i ≥ b k \forall i\in[0,k],a_i\geq b_k i[0,k],aibk

证明:首先, C ( 0 , 0 ) = 1 , C ( 0 , 1 ) = 0 , C ( 1 , 0 ) = 1 , C ( 1 , 1 ) = 1 C(0,0)=1,C(0,1)=0,C(1,0)=1,C(1,1)=1 C(0,0)=1,C(0,1)=0,C(1,0)=1,C(1,1)=1

考虑 C ( n , m ) m o d    2 C(n,m)\mod 2 C(n,m)mod2,则对于 n n n上的为1的位 a i a_i ai b i b_i bi能取0或1,对于 a i = 0 a_i=0 ai=0,则 b i b_i bi必须为0,得证。

例题:HDU4349 Xiao Ming’s Hope 结论就是 2 a i = 1 的 个 数 2^{a_i=1的个数} 2ai=1

(2) 扩展Lucas定理

前置知识:卢卡斯定理 中国剩余定理

扩展Lucas定理用于求解以下公式:

C a b m o d    p C_a^b \mod p Cabmodp,其中 p p p不一定是质数。

由整数的唯一分解定理,对 p p p进行质因数分解, p = p 1 a 1 × p 2 a 2 × . . × p k a k p=p_1^{a_1}\times p_2^{a_2}\times .. \times p_k^{a_k} p=p1a1×p2a2×..×pkak,显然 ∀ i , j ∈ [ 1 , k ] , i ≠ j , ( p i a i , p j a j ) = 1 \forall i,j \in [1,k], i\neq j, (p_i^{a_i},p_j^{a_j})=1 i,j[1,k],i=j,(piai,pjaj)=1

我会在后面的提高篇中证明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值