排列组合初步

排列组合初步

1. 公式部分

  1. 排列数

\(n\)个不同元素中任取\(m\)\(m\leq n\),
\(m\)\(n\)均为自然数)个元素组成一个排列。

排列数公式如下:

\(A_{n}^{m}= \frac{n!}{(n-m)!}\)

理解:第一个位子有\(n\)个选择,第二个位子有\(n-1\)个选择,以此类推,第\(m\)个有\(n-m+1\)个选择,于是得到:

\(A_{n}^{m}=n(n-1)(n-2) \cdots (n-m+1)= \frac{n!}{(n-m)!}\)

显然,\(A_{n}^{n}=n!\)

  1. 组合数

从n个不同的元素中,任选\(m\leq n\)个元素组成一个集和,叫做从 $n \(个不同元素中取出\) m $个元素的一个组合;

从 $n $ 个不同元素中取出 $ m \leq n $ 个元素的所有组合的个数,叫做从 $ n$ 个不同元素中取出 $ m $ 个元素的组合数,用符号 $ \dbinom{n}{m}
$ 来表示,及 $ C_{n}^{m} $ 。

组合数公式如下:

\(\dbinom{n}{m} = C_{n}^{m} = \frac{A_{n}^{m}}{m!} = \frac{n!}{m!(n-m)!}\)

理解:从\(n\)个数选出\(m\)个来,不在乎顺序,据需要 $ A_{n}^{m} $ 去掉重复的。选出\(m\)个人,全排得到\(m!\),所以:\(\dbinom{n}{m} \times m! = A_{n}^{m}\)

规定:\(m>n\)时,\(A_{n}^{m} = \dbinom{n}{m} = 0\)

2. 隔板法(插板法)

  1. \(question 1\)
    现有 $ n $ 个 完全相同 的元素,要求将其分为 $ k $ 组,保证每组至少有一个元素,一共有多少种分法?

考虑拿 $k - 1 $ 块板子插入到 $n $ 个元素两两形成的 $ n - 1 $ 个空里面。
因为元素是完全相同的,所以答案就是
\(\dbinom{n - 1}{k - 1}\)

本质是求 $x_1+x_2+\cdots+x_k=n $ 的正整数解的组数。

  1. \(question 2\)
    如果问题变化一下,每组允许为空呢?

显然此时没法直接插板了,因为有可能出现很多块板子插到一个空里面的情况,非常不好计算。

我们考虑创造条件转化成有限制的问题一,先借 $ k $ 个元素过来,在这 $ n + k $ 个元素形成的 $ n + k - 1 $ 个空里面插板,答案为

\(\binom{n + k - 1}{k - 1} = \binom{n + k - 1}{n}\)
虽然不是直接求的原问题,但这个式子就是原问题的答案,可以这么理解:

开头我们借来了 $ k $ 个元素,用于保证每组至少有一个元素,插完板之后再把这 $ k $ 个借来的元素从 $ k $ 组里面拿走。因为元素是相同的,所以转化过的情况和转化前的情况可以一一对应,答案也就是相等的。

由此可以推导出插板法的公式:
\(\dbinom{n + k - 1}{n}\)

本质是求 $ x_1+x_2+\cdots+x_k=n $ 的非负整数解的组数(即要求 $ x_i \ge 0 $ )。

3. 线性乘法逆元求组合数

逆元

线性求\(1 \sim n\)的逆元

首先,可以显然得到 $ inv_{1} = 1\((\)inv$表示逆元)。

对于任意模数\(p\),有 $ p=k \times i + r $(\(0 \leq r<i\)

此时易得:

\(k\times i+r \equiv 0(mod \ \ p )\)

两边同乘上\(i^{-1} \cdot r^{-1}\),并进行移项:

$ \Rightarrow$ \(i^{-1} \equiv -k \times r^{-1} (mod \ \ p)\)

显然\(r^{-1}\)会先得到。所以:

\(inv_{i}=-\lfloor \frac{p}{i} \rfloor \times inv_{p \ \ mod \ \ i}\)

于是得到:

inv[1]=1;
for(int i=2;i<=6e6;i++)
   inv[i]=(p-p/i)*inv[p%i]%p;

组合数

质数模数求法

$ \dbinom{n}{m}=\frac{n!}{m!(n-m)!}$。

这么好看的式子,显然在模意义可以拆成上下两个 ,上面的直接求取,下面的求逆元就搞定了。

\(O(n)\)预处理逆元和阶乘后,\(O(1)\)计算答案。

code:

线性求阶乘。

fac[0]=1;
for(int i=1;i<=6e6;i++)
    fac[i]=fac[i-1]*i%p;

线性求逆元。

inv[1]=1;
for(int i=2;i<=6e6;i++)
    inv[i]=(p-p/i)*inv[p%i]%p;

线性求阶乘逆元。

ifac[0]=1;
for(int i=1;i<=6e6;i++)
    ifac[i]=ifac[i-1]*inv[i]%p;

组合数计数。

int C(int a,int b)
{
    if(a==b)return 1;
    if(a<b||b<0) return 0;
    return fac[a]*ifac[a-b]%p*ifac[m]%p;
}

参考1

参考2:Oi-Wiki

待续

1. 乘法逆元求组合数(快速幂版)

2. Lucas定理求组合数

posted @ 2024-12-31 20:52  Nightmares_oi  阅读(26)  评论(0编辑  收藏  举报