笔记——排列组合
蓝月の笔记——排列组合篇
摘要
万恶的数学!
Part 1 加乘原理
小学奥数内容
加法原理:当多个方案并列(即互不影响)时,总方案数为各个方案数之和
例:共有 \(k\) 种交通工具可以从A地到B地,第 \(i\) 种交通工具有 \(a_i\) 班次,那么从A地到B地的总方案数为 \(\displaystyle\sum_{i=1}^{k} a_i\)
乘法原理:当多个方案分步完成时,总方案数为各步方案数之积
例:从 \(1\) 地到 \(k\) 地,规定只能从 \(i\) 地单项移动到 \(i+1\) 地,且有 \(a_i\) 种路线,那么从 \(1\) 地到 \(k\) 地的总方案数为 \(\displaystyle\prod_{i=1}^{k-1}a_i\)
Part 2 排列组合数
排列数
从 \(n\) 个小朋友种选择 \(k\) 个小朋友排成一队的方法数,记为 \(\mathrm{A}_n^k\)
公式推导
选第 \(1\) 个小朋友有 \(n\) 种方案,选第 \(2\) 个小朋友有 \(n-1\) 种方案(因为有一个被选了),\(\cdots\),选第 \(k\) 个小朋友有 \(n-k+1\) 种方案
根据乘法原理,\(\mathrm{A}_n^k=\displaystyle\prod_{i=n-k+1}^{n} i=\large\frac{n!}{(n-k)!}\)
组合数
从 \(n\) 个小朋友种选择 \(k\) 个小朋友的方法数(不考虑顺序),记为 \(\mathrm{C}_n^k\)
公式推导
选出 \(k\) 个不考虑顺序的小朋友,相当于选出 \(k\) 个考虑顺序的小朋友再打乱顺序,除以顺序数即可
\(\mathrm{C}_n^k = \large\frac{\mathrm{A}_n^k}{k!}=\large\frac{n!}{(n-k)!k!}\)
对称性
在 \(n\) 个里面选 \(k\) 个相当于在 \(n\) 个里面选 \(n-k\) 个丢掉,所以 \(\mathrm{C}_{n}^{k}=\mathrm{C}_{n}^{n-k}\)
多重组合数
实际上是全排列,有 \(k\) 对多胞胎,共 \(n\) 个人,第 \(i\) 对有 \(a_i\) 个人,因为每一对多胞胎之间长得太像了,所以在给他们排队时会把他们是为同一个人(即交换位置只算做一种方案),排队的方案数即为多重组合数,记为 \(\large{n \choose {a_1,a_2,\cdots,a_k}}\)
公式推导
假设多胞胎交换位置算作多种方案,答案显然为 \(n!\)
每一对多胞胎之间都有 \(a_i!\) 种先后位置关系,将这些重复计算除掉即可,\({n \choose {a_1,a_2,\cdots,a_k}}=\frac{n!}{\displaystyle\prod_{i=1}^{k}a_i!}\)
Part 3 插板法
可以理解为求不定方程 \(\displaystyle\sum_{i=1}^{k} x_i=n\) 的解的数量
\(x_i > 0\)
在 \(n\) 个 \(1\) 之间的 \(n-1\) 空插 \(k-1\) 个板子,分成的 \(k\) 个空间的 \(1\) 的个数为每一个 \(x_i\) 的值
答案为 \(\mathrm{C}_{n-1}^{k-1}\)
\(x_i \ge 0\)
在 \(n\) 个 \(1\) 后补 \(k\) 个 \(1\),再插 \(k-1\) 个板子,最后拿掉补的 \(k\) 个 \(1\),就可以留出 \(0\) 个 \(1\) 的区间了
答案为 \(\mathrm{C}_{n+k-1}^{k-1}=\mathrm{C}_{n+k-1}^{n}\)(对称性)
\(x_i \ge a_i\)
令 \(t=\sum a_i,s_i=x_i-a_i\)
在 \(n\) 个 \(1\) 后补 \(t\) 个 \(1\),此时可以保证 \(k\) 个区间内都达到了 \(x_i \ge a_i\) 的要求,那么原方程转化为 \(\displaystyle\sum_{i=1}^{k} (s_i+a_i)=n\),则 \(\displaystyle\sum_{i=1}^{k} s_i=n-t(s_i \ge 0)\),变成了 \(x_i \ge 0\) 的形式,按公式求解即可
答案为 \(\mathrm{C}_{n-t+k-1}^{n-t}\)
Part 4 杨辉三角&二项式定理
杨辉三角:
通过瞪眼法可以知道,杨辉三角的第 \(i\) 行 \(j\) 个数为 \(\mathrm{C}_{i-1}^{j-1}\)
再次通过瞪眼法可以知道,杨辉三角最左一列和最右一列都是 \(1\),其它数都是它左上的数和右上的数的和,那么组合数的递推式就是 \(\mathrm{C}_{i}^{j}=\mathrm{C}_{i-1}^{j}+\mathrm{C}_{i-1}^{j-1}\)
再再次通过瞪眼法,可以发现杨辉三角的第 \(i\) 列为 \((x+y)^{i-1}\) 展开后每一项的系数,这就是二项式定理,用公式表示为:
将二项式推广为多项式,同理可得 \((\displaystyle\sum_{i=1}^{k}x_i)^n=\sum_{(a_1,a_2,\cdots,a_k)满足性质P}({n \choose {a_1,a_2,\cdots,a_k}}\displaystyle\prod_{i=1}^{k}x_i^{a_i})\)
我们称 \(k\) 元组 \((a_1,a_2,\cdots,a_k)\) 满足性质 \(P\),当且仅当 \(\forall i \in [1,k],x_i=a_i\) 是不定方程 \(\sum_{1 \le i \le k}x_i=n\) 的一组解
Part 5 圆排&项链排&错排
圆排列
\(n\) 个人坐在一个圆桌上吃饭,排位置的方案数就是圆排列数(旋转前后算一种方案)
显然为全排列除以旋转次数,\(R_{n}=\frac{\mathrm{A}_n^n}{n}=(n-1)!\)
项链排列
\(n\) 个不同的珠子串成一个项链,可以串成的项链数量就是项链排列数(旋转、翻转前后算一种方案)
在圆排列的基础上除以两种翻转方式,\(N_n=\frac{R_n}{2}=\frac{(n-1)!}{2}\)
错位排列(信封问题)
例题终于有题了
令 \(p\) 是 \(1 \sim n\) 的一个排列,如果 \(\forall i \in [1,n],p_i \not= i\),那么我们称 \(p\) 是 \(n\) 的一个错位排列
错排数可以使用递推求解,Oi Wiki上已经讲的很清楚了,我就(懒)不(得)讲了
Code:
d[0] = 1, d[1] = 0, d[2] = 1;
for (int i = 3; i < kMaxN; i++) {
d[i] = ((i - 1) * ((d[i - 1] + d[i - 2]) % kP)) % kP;
}
Part 6 抽屉原理
小学奥数内容
有 \(n\) 个抽屉和 \(n+1\) 个小朋友,把小朋友放到抽屉里(?),至少有一个抽屉里有 \(2\) 个小朋友,证明留作课后习题
推广后同理可得,有 \(k\) 个抽屉和 \(n\) 个小朋友,再把小朋友放到抽屉里,则至少有一个抽屉里有 \(\lceil\frac{n}{k}\rceil\) 个小朋友,证明留作课后习题 \(2\)
Part 7 容斥原理
怎么tm还是小学奥数内容
有 \(x\) 个小朋友是男的,有 \(y\) 个小朋友是女的,有 \(z\) 个小朋友既是男的也是女的(?),则小朋友的数量就是 \(x+y-z\),证明如图显然
用集合形式表示即为 \(\lvert A \cup B \rvert = \lvert A \rvert + \lvert B \rvert - \lvert A \cap B \rvert\)
推广到一般形式:\(\lvert \displaystyle\bigcup^{n}_{i=1} A_i \rvert = \displaystyle\sum_{p=1}^{m}((-1)^{p-1}\displaystyle\sum_{1 \le r_1 \le r_2 \le \cdots \le r_p \le m}\lvert \displaystyle\bigcap_{i=1}^{p} A_{r_i} \rvert)\)
先使用背包求出无限制的情况下所有价格的方案数,再求出用每一种硬币付不起的最小价格,最后按公式容斥即可(位运算是个好东西,可惜我不会)
// BLuemoon_
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int kMaxN = 1e5 + 5, kN = 4 + 5;
LL n, t, c[kN], a[kN], s, dp[kMaxN], f[kN], ans;
int main() {
cin >> c[1] >> c[2] >> c[3] >> c[4], dp[0] = 1;
for (int i = 1; i <= 4; i++) {
for (int j = c[i]; j <= 1e5; j++) {
dp[j] += dp[j - c[i]];
}
}
for (cin >> t; t; t--, ans = f[5] = 0) {
cin >> a[1] >> a[2] >> a[3] >> a[4] >> s, ans += dp[s];
for (int i = 1; i <= 4; i++) {
f[i] = c[i] * (a[i] + 1), (s >= f[i]) && (ans -= dp[s - f[i]]), f[5] += f[i];
}
for (int i = 1; i <= 3; i++) {
for (int j = i + 1; j <= 4; j++) {
(s >= (f[i] + f[j])) && (ans += dp[s - f[i] - f[j]]);
}
}
for (int i = 1; i <= 2; i++) {
for (int j = i + 1; j <= 3; j++) {
for (int l = j + 1; l <= 4; l++) {
(s >= (f[i] + f[j] + f[l])) && (ans -= dp[s - f[i] - f[j] - f[l]]);
}
}
}
(s >= f[5]) && (ans += dp[s - f[5]]);
cout << ans << '\n';
}
return 0;
}
Part 8 卡特兰数
我们来看一些问题:
1.
在坐标系中从 \((0,0) \rightarrow (n,n)\),只能向右或向上走,且不能超过 \(l:y=x\),问有多少种路径
2.
圆上有 \(2n\) 个均匀排列的点,将它们两两相连得到 \(n\) 条弦,问有多少种连接方式使这 \(n\) 条弦互不相交
3.
\(n\) 个节点可以构造的二叉树个数
这些问题的答案都是卡特兰数,写作 \(H_n\),我们知道 \(H_0=1,H_1=1,H_2=2,H_3=5,H_4=14\)