一、两个原理
加法原理
若完成一件事的的方法有 类,其中第 类方法的包括 种不同的方法,且这些方法互不重合,则完成这件事共有: 种不同的方法。
乘法原理(分布计数原理)
若完成一件事需要 个步骤,其中第 个步骤有 种不同的完成方法,且这些步骤互不干扰,则完成这件事共有: 中不同的方法。
两个原理的区别:一个与分类有关,一个与分布有关;加法原理是"分类完成",乘法原理是"分步完成"。
二、排列及其公式
1.线排列
-
Ⅰ.定义:一般地,从 个不同的元素中,取出 个元素按照一定的顺序拍成一列,叫做从 个不同的元素中取出 个元素的一个线排列。从 个不同元素中取出 个元素的所有线排列的个数,叫做从 个不同元素中取出 个元素的排列数,用符号 或者 表示。
-
Ⅱ.排列数公式:
-
全排列:从 个不同的元素中取出 个元素的一个线排列,叫做 个元素的全排列数,用 来表示。此时,
2.相异元素可重排列
从 个不同元素中可以重复地选出 个元素的排列,叫做相异元素的可重复排列。其排列总数为 。
3.不全相异元素的排列
如果在 个元素中,有 个元素彼此相同,有 和元素彼此相同…有 彼此相同,并且 ,则这 个元素的全排列叫做不全相异元素的全排列。
其排列数公式为: 。
【引例】把3个相同的黄球,2个相同的蓝球,4个相同的白球排成一排,问:有多少种不同的排法?
解:符合不全相异元素的排列,使用不全相异元素的排列公式: 种。
4.圆排列
从 个不同元素中选取出 个元素,部分首尾地排成一个圆圈的排列叫做圆排列,其排列方案数为:
三、组合及其公式
1.非重组合
- Ⅰ.定义: 一般地,从 个不同的元素中,取出 个元素,不允许元素重复,不考虑次序,叫做从 个不同的元素中取出 个元素的一个非重组合;从 个不同元素中取出 个元素的所有组合的个数,叫做从 个不同个元素中取出 个元素的组合数,用符号 表示。根据乘法原理(分布计数原理)可知: 。
- Ⅱ.组合数公式:
- Ⅲ.组合数的三个性质:
1、 ,规定:
2、
3、
2.可重复组合
从 个不同元素中,取出 个元素组成一个组合,且允许这 个元素重复使用(一般 ,但也允许 ),则称这样的组合为可重复组合,器组合数记为 个
3.二项式定理
【例题1】luogu P1313 计算系数 (NOIP 2011)
题意
给定一个多项式
,请求出多项式展开后
项的系数。
思路
直接二项式定理分解。
#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 进制数
题意
设
是个
进制数,并满足以下条件:
(1) 至少是个 位的 进制数。
(2)作为 进制数,除最后一位外, 的每一位严格小于它右边相邻的那一位。
(3)将 转换为 进制数 后,则 的总位数不超过 。
在这里,正整数 和 是事先给定的。
问:满足上述条件的不同的
共有多少个?
思路
四、多重集
1.多重集的排列数
多重集是指包括重复元素的广义集合。设
是由
个
,
个
,
个
,…,
个
组成的多重集。
的全排列个数为:
2.多重集的组合数
设
是由
个
,
个
,
个
,…,
个
组成的多重集。设整数
从
中取出
个元素组成一个多重集(不考虑元素的顺序),产生的不同多重集的数量为:
证明:
原问题等价于统计下列集合的数量: ,其中 并且 。因为 ,必定有 ,所以只需要考虑 这一条件。
故原问题等价于 个0, 个 构成的全排列数—— 个 把 个 分成 组,每组的 的数量对应 。而多重集 的全排列数为:
。
五、Lucas定理
作用: 利用模运算快速求出二项式系数C(n,m),适用于不用模运算就无法求出其结果的、具有非常大的n和m的二项式系数。
若
是质数,则对于任意整数
,有:
也就是把
和
表示成
进制数,对
进制下的每一位分别计算组合数,最后再乘起来。
【例题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数列
给定 个 和 个1,它们按照某种顺序排成长度为 的序列,满足任意前缀中 的个数都不少于 的个数的序列的数量为 。
证明: 令 个 和 个 任意排成一个长度为 的序列 ,若 不满足任意前缀中 的个数都不少于 的个数,则存在一个最小的位置 ,使得 中有 个 , 个 。而把 中的所有数位取反后,包含 个 和 个 .于是我们得到了有 个 和 个 排成的序列。
同理,令 个 和 个 随意排成一个长度为 的序列 ,也必定存在一个最小的位置 ,使得 中有 个 , 个 。把 后面剩下的一半取反,就得到了由 个 和 个 排成的、存在一个前缀 比 多的序列。
因此,以下两种序列构成一个双射:
- Ⅰ.由 个 和 个 排成的、存在一个前缀 比 多的序列。
- Ⅱ.由
个
和
个
排成的序列。
根据组合数的定义,后者显然有 个。
综上所述,由 个 和 个 排成的、任意前缀中 都不少于 的序列数量为:
与Catalan数相关的问题:
- Ⅰ. 个左括号和 个右括号组成的合法括号的数量为 。
- Ⅱ. 经过一个栈,形成的合法出栈序列的数量为 。
- Ⅲ. 个节点构成的不同二叉树的数量为 。
- Ⅳ.在平面直角坐标系中,每一步只能向上或者向右走,从 走到 并且除两个端点外不结束直线 的线路数量为 。