快乐地打牢基础(11)——组合数学

一、两个原理


加法原理

若完成一件事的的方法有 n n 类,其中第 i i 类方法的包括 m i m_i 种不同的方法,且这些方法互不重合,则完成这件事共有: N = m 1 + m 2 + . . . + m n N= m_1+m_2+...+m_n 种不同的方法。

乘法原理(分布计数原理)

若完成一件事需要 n n 个步骤,其中第 i i 个步骤有 m i m_i 种不同的完成方法,且这些步骤互不干扰,则完成这件事共有: N = m 1 × m 2 × . . . × m i N = m_1 \times m_2\times...\times m_i 中不同的方法。

两个原理的区别:一个与分类有关,一个与分布有关;加法原理是"分类完成",乘法原理是"分步完成"。

二、排列及其公式


1.线排列

  • Ⅰ.定义:一般地,从 n n 个不同的元素中,取出 m ( m n ) m(m\leq n) 个元素按照一定的顺序拍成一列,叫做从 n n 个不同的元素中取出 m m 个元素的一个线排列。从 n n 个不同元素中取出 m m 个元素的所有线排列的个数,叫做从 n n 个不同元素中取出 m m 个元素的排列数,用符号 A ( P ) ( n , m ) A(P)(n,m) 或者 A ( P ) n m A(P)^m_n 表示。

  • Ⅱ.排列数公式: A n m = n ( n 1 ) ( n 2 ) . . . ( N m + )   n ! ( n m ) ! A_n^m = n(n-1)(n-2)...(N- m+1) =\frac{n!}{(n-m)!}

  • 全排列:从 n n 个不同的元素中取出 n n 个元素的一个线排列,叫做 n n 个元素的全排列数,用 A n n A_n^n 来表示。此时, A n n = n ( n 1 ) × ( n 2 ) × . . × 3 × 2 × 1 = n ! . A_n^n = n(n-1)\times (n-2)\times..\times3\times2\times1=n!.

2.相异元素可重排列

n n 个不同元素中可以重复地选出 m m 个元素的排列,叫做相异元素的可重复排列。其排列总数为 n m n^m

3.不全相异元素的排列

如果在 n n 个元素中,有 n 1 n_1 个元素彼此相同,有 n 2 n_2 和元素彼此相同…有 n m n_m 彼此相同,并且 n 1 + n 2 + . . . + n m = n n_1+n_2+...+n_m=n ,则这 n n 个元素的全排列叫做不全相异元素的全排列。

其排列数公式为: n ! n 1 ! × n 2 ! × . . . × n m ! {\frac{n!}{n_1!\times n_2!\times ...\times n_m!}}

【引例】把3个相同的黄球,2个相同的蓝球,4个相同的白球排成一排,问:有多少种不同的排法?
解:符合不全相异元素的排列,使用不全相异元素的排列公式: ( 3 + 2 + 4 ) ! 3 ! × 2 ! × 4 ! = 1269 \frac{(3+2+4)!}{3!\times2!\times4!}=1269 种。

4.圆排列

n n 个不同元素中选取出 m m 个元素,部分首尾地排成一个圆圈的排列叫做圆排列,其排列方案数为: A n m m = n ! m × ( n m ) ! {\frac{A_n^m}{m}=\frac{n!}{m\times(n-m)!}}

三、组合及其公式


1.非重组合

  • Ⅰ.定义: 一般地,从 n n 个不同的元素中,取出 m m n m(m\leq n) 个元素,不允许元素重复,不考虑次序,叫做从 n n 个不同的元素中取出 m m 个元素的一个非重组合;从 n n 个不同元素中取出 m m n m(m\leq n) 个元素的所有组合的个数,叫做从 n n 个不同个元素中取出 m m 个元素的组合数,用符号 C n m C_n^m 表示。根据乘法原理(分布计数原理)可知: A n m = C n m × A m m A_n^m = C_n^m\times A_m^m
  • Ⅱ.组合数公式: C n m = A n m A m m = n ( n 1 ) ( n 2 ) . . . ( n m + 1 ) m ! = n ! m ! ( n m ) ! ( n , m N ) C_n^m = \frac{A_n^m}{A_m^m} = \frac{n(n-1)(n-2)...(n-m+1)}{m!}=\frac{n!}{m!(n-m)!}(n,m\in N^{*})
  • Ⅲ.组合数的三个性质:
    1、 C n m = C n n m C_n ^ {m}=C_n^{n-m} ,规定: C n 0 = 1 , C n n = 1 C_n^0=1,C_n^n = 1
    2、 C n + 1 m = C n m + C n m 1 C_{n+1}^{m}=C_n^m+C_n^{m-1}
    3、 C n m = n m + 1 m × C n m 1 C_n ^ {m} = \frac{n- m + 1}{m}\times C_n^{m-1}

2.可重复组合

n n 个不同元素中,取出 r r 个元素组成一个组合,且允许这 r r 个元素重复使用(一般 r n r\leq n ,但也允许 r > n r >n ),则称这样的组合为可重复组合,器组合数记为 H n , r H n , r = C n + r 1 r H(n,r),H(n,r) = C_{n+ r - 1}^{r}

3.二项式定理

( a + b ) n = k = 0 n C n k a n k b k (a+b)^n = \displaystyle\sum_{k= 0}^nC_n^ka^{n-k}b^k

【例题1】luogu P1313 计算系数 (NOIP 2011)
题意
给定一个多项式 ( a x + b y ) k (ax + by)^k ,请求出多项式展开后 x n × y m x^n \times y^m 项的系数。
思路
直接二项式定理分解。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;

const int Max = 1e4 + 10;
const int mod = 10007;

ll qpow(ll a,ll b){
    int res = 1;
    while(b){
        if(b & 1){
            res = res * a % mod;
        }
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
int c[Max] = {0};
int main(){
    ll a,b,k,n,m;
    ll ans = 1;
    scanf("%lld %lld %lld %lld %lld",&a,&b,&k,&n,&m);
    c[1] = k % mod;
    for(int i = 2; i <= k; i++){
        c[k - i] = c[i] = (k - i + 1) % mod * c[i-1] % mod  * qpow(i,mod-2) % mod;
    }
    ans  = c[m] % mod * qpow(a,n) % mod * qpow(b,m) % mod;
    printf("%lld\n",ans);

}

【例题】luogu P1066 2 k 2^k 进制数

题意
r r 是个 2 k 2^k 进制数,并满足以下条件:

(1) r r 至少是个 2 2 位的 2 k 2^k 进制数。

(2)作为 2 k 2^k 进制数,除最后一位外, r r 的每一位严格小于它右边相邻的那一位。

(3)将 r r 转换为 2 2 进制数 q q 后,则 q q 的总位数不超过 w w

在这里,正整数 k ( 1 k 9 ) k(1≤k≤9) w ( k < W 30000 ) w(k<W≤30000) 是事先给定的。

问:满足上述条件的不同的 r r 共有多少个?
思路

四、多重集


1.多重集的排列数

多重集是指包括重复元素的广义集合。设 S = { n 1 a 1 n 2 a 2 , n 3 a 3 , . . . , n k a k } S = \{n_1·a_1,n_2·a_2,n_3·a_3,...,n_k·a_k\} 是由 n 1 n_1 a 1 a_1 , n 2 n_2 a 2 a_2 , n 3 n_3 a 3 a_3 ,…, n k n_k a k a_k 组成的多重集。
S S 的全排列个数为:
n ! n 1 ! n 2 ! . . . n k ! \frac{n!}{n_1!n_2!...n_k!}

2.多重集的组合数

S = { n 1 a 1 n 2 a 2 , n 3 a 3 , . . . , n k a k } S = \{n_1·a_1,n_2·a_2,n_3·a_3,...,n_k·a_k\} 是由 n 1 n_1 a 1 a_1 , n 2 n_2 a 2 a_2 , n 3 n_3 a 3 a_3 ,…, n k n_k a k a_k 组成的多重集。设整数 r n i ( [ 1 , k ] ) r\leq n_i(\forall\in[1,k])。 S S 中取出 r r 个元素组成一个多重集(不考虑元素的顺序),产生的不同多重集的数量为:
C k + r + 1 k 1 C_{k+r+1}^{k-1}

证明:
原问题等价于统计下列集合的数量: { x 1 a 1 x 2 a 2 , x 3 a 3 , . . . , x k a k } \{x_1·a_1,x_2·a_2,x_3·a_3,...,x_k·a_k\} ,其中 I = 1 k x i = r \sum^k_{I= 1}x_i=r 并且 x i n i x_i\leq n_i 。因为 r n i r \leq n_i ,必定有 x i n i x_i \leq n_i ,所以只需要考虑 I = 1 k x i = r \sum^k_{I= 1}x_i=r 这一条件。

故原问题等价于 r r 个0, k 1 k-1 1 1 构成的全排列数—— k 1 k-1 1 1 r r 0 0 分成 k k 组,每组的 0 0 的数量对应 x i x_i 。而多重集 { r 0 , ( k 1 ) 1 } \{r·0,(k-1)·1\} 的全排列数为:
( r + k 1 ) ! r ! ( k 1 ) ! = C k + r 1 r = C k + r 1 k 1 \frac{(r+k-1)!}{r!(k-1)!}=C_{k+r-1}^{r}=C_{k+r-1}^{k-1}

五、Lucas定理


作用: 利用模运算快速求出二项式系数C(n,m),适用于不用模运算就无法求出其结果的、具有非常大的n和m的二项式系数。

p p 是质数,则对于任意整数 1 m n 1 \leq m\leq n ,有:
C n m = C n   m o d   p m   m o d   p × C n / p m / p ( m o d   p ) C_n^m = C_{n \ mod\ p}^{m\ mod \ p}\times C_{n/p}^{m/p}(mod\ p)
也就是把 n n m m 表示成 p p 进制数,对 p p 进制下的每一位分别计算组合数,最后再乘起来。
【例题3】一本通OJ 1650:组合
题意

给出组合数 C(n,m) 表示从 n 个元素中选出 m 个元素的方案数。例如 C(5,2)=10,C(4,2)=6。可是当 n,m 比较大的时候,C(n,m) 很大。于是 xiaobo 希望你输出 C(n,m) mod p 的值。

思路
求组合数,但因为组合数过大,并且需要取模,所以考虑Lucas定理。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;

ll n,m,p,cas;

ll qpow(ll a,ll b,ll mod){
	ll res = 1;
	while(b){
		if(1 & b){
			res  = res * a % mod;
		}
		a = a * a % mod;
		b >>= 1; 
	}
	return res;
}

ll C(ll n,ll m,ll mod){
	if(m > n)
		return 0;
	if(m == 1)
		return n;
	if(n == m || m == 0)
		return 1;
//	ll res1 = 1,res2 = 1,res3 = 1;
//	for(int i = 1; i <= n; i++){
//		res1 = res1 * i % mod;
//		if(i <= m)
//		res2 = res2 * i % mod;
//		if(i <= n - m)
//		res3 = res3 * i % mod;
//	} 
//	return res1 * qpow(res2 * res3 % mod , mod - 2,mod) % mod;
	ll res1 = 1,res2 = 1,res3 = 1;
	for(int i = n - m + 1; i <= n; i++)
		res1 = res1 * i % mod;
	for(int i = 1; i <= m; i++)
		res2 = res2 * i % mod;
	return res1 * qpow(res2  , mod - 2,mod) % mod;
}
ll Lucas(ll n,ll m, ll mod){
	if(!m)
		return 1;
	return C(n % mod,m % mod,mod) * Lucas(n / mod , m / mod, mod) % mod;
}
int main(){
	scanf("%lld",&cas);
	while(cas--){
		scanf("%lld %lld %lld",&n,&m,&p);
		printf("%lld\n",Lucas(n,m,p));
	}
	return 0;
} 

六、Catalan数列


给定 n n 0 0 n n 个1,它们按照某种顺序排成长度为 2 n 2n 的序列,满足任意前缀中 0 0 的个数都不少于 1 1 的个数的序列的数量为 C a t n = 1 n + 1 C 2 n n Cat_n = \frac{1}{n+1}C_{2n}^{n}

证明: n n 0 0 n n 1 1 任意排成一个长度为 2 n 2n 的序列 S S ,若 S S 不满足任意前缀中 0 0 的个数都不少于 1 1 的个数,则存在一个最小的位置 2 p + 1 [ 1 , 2 n ] 2p+1\in [1,2n] ,使得 S [ 1 2 p + 1 ] S[1~2p+1] 中有 p p 0 0 , p + 1 p+1 1 1 。而把 S [ 2 p + 2 2 n ] S[2p+2~2n] 中的所有数位取反后,包含 n p 1 n-p-1 0 0 n p n-p 1 1 .于是我们得到了有 n 1 n-1 0 0 n + 1 n+1 1 1 排成的序列。
同理,令 n 1 n-1 0 0 n + 1 n+1 1 1 随意排成一个长度为 2 n 2n 的序列 S S ,也必定存在一个最小的位置 2 p + 1 2p+1 ,使得 S [ 1 2 p + 1 ] S[1~2p+1] 中有 p p 0 0 p + 1 p+1 1 1 。把 S S 后面剩下的一半取反,就得到了由 n n 0 0 n n 1 1 排成的、存在一个前缀 0 0 1 1 多的序列。

因此,以下两种序列构成一个双射:

  • Ⅰ.由 n n 0 0 n n 1 1 排成的、存在一个前缀 0 0 1 1 多的序列。
  • Ⅱ.由 n 1 n-1 0 0 n + 1 n+1 1 1 排成的序列。
    根据组合数的定义,后者显然有 C 2 n n 1 C_{2n}^{n-1} 个。
    综上所述,由 n n 0 0 n n 1 1 排成的、任意前缀中 0 0 都不少于 1 1 的序列数量为:
    C 2 n n C n 1 2 n = ( 2 n ) ! n !   n ! ( 2 n ) ! ( n 1 ) ! ( n 1 ) ! = 1 n + 1 C 2 n n = C a t n C_{2n}^{n}-C_{n-1}^{2n} = \frac{(2n)!}{n!\ n!}-\frac{(2n)!}{(n-1)!(n-1)!}=\frac{1}{n+1}C_{2n}^{n} = Cat_n

与Catalan数相关的问题:

  • Ⅰ. n n 个左括号和 n n 个右括号组成的合法括号的数量为 C a t n Cat_n
  • Ⅱ. 1 , 2 , . . . n 1,2,...n 经过一个栈,形成的合法出栈序列的数量为 C a t n Cat_n
  • Ⅲ. n n 个节点构成的不同二叉树的数量为 C a t n Cat_n
  • Ⅳ.在平面直角坐标系中,每一步只能向上或者向右走,从 0 , 0 (0,0) 走到 n n (n,n) 并且除两个端点外不结束直线 y = x y=x 的线路数量为 2 C a t n 2Cat_n
发布了141 篇原创文章 · 获赞 71 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/sinat_40872274/article/details/99861033