OI数学总结-组合数学
施工中,咕咕咕….
排列组合
更多详见 小蓝书 16.1
下面两个指的是无重复的排列与组合
排列数
从 $n$ 个不同元素中取 $k(k\le n)$ 个不同元素,并按一定顺序排成一列的方案数
也可写作 $\mathrm{P}_{n}^{k}$ 或直接用组合数写法 $k! \times \binom{n}{k}$
代码求解
$O(n)$ 计算
例如求解 $A_{n-m+1}^{m} \bmod p$
int res=1;
for(int i=n-m+1; i>=n-2*m+2; i--)
res=res*i%p;
cout << res << '\n'
组合数
从 $n$ 个不同元素中取 $k(k \le n)$ 个不同元素的方案数
高中一般记作 $\mathrm{C}_n^{k}$ ,不是很推荐使用。
特别地,规定当 $k > n$ 时, $\mathrm{A}_n^{k} = \mathrm{C}_n^{k}=0$
代码求解
$O(n^2)$ 递推
for(int i=0; i<=1000; i++)
{
C[i][0]=1;
for(int j=1; j<=i; j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
}
$O(p)$ Lucas定理
详见 [OI数学总结-数论]
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(2e5+15)
int Q,n,m,p;
int fac[N];
int qpow(int a,int b,int p)
{
int ans=1,base=a;
while(b)
{
if(b&1)ans=ans*base%p;
base=base*base%p;
b>>=1;
}
return ans;
}
// n 选 m
int C(int n,int m,int p)
{
if(n<m)return 0;
return fac[n]*qpow(fac[m],p-2,p)%p*qpow(fac[n-m],p-2,p)%p;
}
int lucas(int n,int m,int p)
{
if(!m)return 1;
return lucas(n/p,m/p,p)*C(n%p,m%p,p)%p;
}
signed main()
{
cin >> Q;
while(Q--)
{
cin >> n >> m >> p;
fac[1]=fac[0]=1;
for(int i=2; i<=n+m; i++)
fac[i]=fac[i-1]*i%p;
cout << lucas(n+m,m,p) << '\n'; // C(n+m,n)
}
return 0;
}
排列组合常用公式
这个可以单独拉出来讲,到时候写一个。
$(2)$ 可以通过杨辉三角(数学归纳法)简单证明。
证明的话直接移项,然后一直根据递推式合并即可。
容斥原理
容斥原理(principle of inclusion-exclusion):令 $X$ 为一个有限集合,$P_1, P_2,\dots , P_m$ 是一些性质的集合,对于任意 $S \subseteq [m]$ ,即 $S\subseteq \{1,2,\cdots,m\}$ ,定义 $N(S) = \{x \in X \mid \forall i \in S : x \text{ has } P_i\}$ 。那么 $X$ 中满足性质 $P_{1\sim m}$ 的元素个数为
如果是不满足任何一个性质的话,把指数中的 $|S|+1$ 改成 $|S|$ 即可。
特别地,对于任意的 $S \subseteq [m]$ ,如果 $N(S)$ 只和 $S$ 的大小(即 $|S|$ )有关,那么式子可以化简为
其中 $N(i)$ 表示对于任意满足 $S \subseteq [m],|S|=i$ 的 $N(S)$ 值。
如果是不满足任何一个性质的话,把指数中的 $i+1$ 改成 $i$ 即可。
容斥原理的常用技巧:
定义一些“坏性质” $P_1,\dots,P_m$
找到对于每个 $S\subseteq [m]$ 计算 $N(S)$ 的方法
套用容斥原理计算不满足任何一个“坏性质”的元素个数
注意这里求的是不满足任何坏性质,因此用以下公式
鸽巢原理(抽屉原理)
假如有 $n+1$个元素放到 $n$ 个集合中去,其中必定有一个集合里至少有两个元素。
应用
- 任意 $n+1$ 个数中至少有 $2$ 个数与 $n$ 同余
第二类斯特林数
第二类斯特林数记作 $S(n,k)$ 或 $\left\{\begin{array}{l}n \\k\end{array}\right\}$ 。
表示将 $n$ 个两两不同的元素划分为 $k$ 个无区别的非空子集的方案数。
换个说法,$n$ 个不同的球放到 $m$ 个相同的盒子,不可以有空盒子的方案数。
注意,如果是 $n$ 个相同的球,那就是插板法的问题了,所以不同的球是第二类斯特林数的关键。
边界:
递推式:
鉴于我每次都记错这个递推式,我要着重讲一下它的含义。
$\left\{\begin{array}{l}n-1 \\k-1\end{array}\right\}$ 表示把第 $n$ 个球单独放在新的第 $k$ 个盒子里。
$k\left\{\begin{array}{c}n-1\\k\end{array}\right\}$ 表示把第 $n$ 个球放在任意盒子里,并且此时盒子数量仍然是 $k$ 。
可能写的有点奇怪,但是熟悉递推的同学应该知道我在说什么吧。
第一类斯特林数
第一类斯特林数记作 $s(n,k)$ 或 $\left[\begin{array}{l}n \\k\end{array}\right]$ 。
表示将 $n$ 个两两不同的元素划分为 $k$ 个互不区分的非空轮换的方案数。
换个说法,$n$ 个不同的数恰好划分为 $k$ 个圆排列的方案数
边界:
递推式:
伯努利数
常用于等幂求和( $m$ 次幂和的公式)
记 $S_m(n) = \sum\limits_{i=0}^{n-1} i^m$ ,则
其中 $B_k$ 为伯努利数,定义如下
例如 $\binom{2}{0} B_0 = \binom{2}{1}B_1 = 0$
$n$ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | $\dots$ |
---|---|---|---|---|---|---|---|---|---|
$B_n$ | $1$ | $-\frac{1}{2}$ | $\frac{1}{6}$ | $0$ | $-\frac{1}{30}$ | $0$ | $\frac{1}{42}$ | $0$ | $\dots$ |
代码求解
咕咕咕…
贝尔数(Bell数)
$B_n$ 是基数为 $n$ 的集合的划分方法的数目
集合 $S$ 的一个划分定义为 $S$ 的两两不相交的非空子集的族,它们的并为 $S$
例如 $B_3=5$ ,因为 $3$ 个元素的集合 $a,b,c$ 有 $5$ 种不同的划分方法
贝尔数适合递推公式
贝尔三角形(类似于杨辉三角)
则有 $B_n = A_{n,1}$
代码求解
咕咕咕….