组合
组合数
重要公式
- \(\begin{pmatrix}a\\b\end{pmatrix}\) \(\begin{pmatrix}b\\c\end{pmatrix}=\)\(\begin{pmatrix}a\\c\end{pmatrix}\)\(\begin{pmatrix}a-c\\b-c\end{pmatrix}\)
- \(\sum\limits_{i=r}^n\begin{pmatrix}i\\r\end{pmatrix}=\begin{pmatrix}n+1\\r+1\end{pmatrix}\)
- \(\dfrac{a^{\underline{i}}}{b^{\underline{i}}}=\dfrac{\begin{pmatrix}a\\i\end{pmatrix}}{\begin{pmatrix}b\\i\end{pmatrix}}=\dfrac{\begin{pmatrix}b-i\\b-a\end{pmatrix}}{\begin{pmatrix}b\\a\end{pmatrix}}\)
- \((x+y)^n=\sum\limits_{i=0}^{n}\begin{pmatrix}n\\i\end{pmatrix}x^iy^{n-i}\)
CF660E Different Subsets For All Tuples
思路显然,计算每种子序列的贡献。注意概念,子序列不需要连续。
空子序列,\(m^n\)。
非空子序列,只统计第一次出现的位置。第一维 \(i\) 枚举长度,第二维 \(j\) 枚举结尾位置。结尾位置确定后,就要在前 \(j-1\) 个位置里面确定 \(i-1\) 个位置来填充了。然后在这前 \(j\) 个位置中,非子序列位置,不能出现子序列下一个要出现的数字,因此只能有 \(m-1\) 种选择,剩下的随便填。
观察形式,尝试凑出二项式定理,
\(O(n\log n)\) 计算即可。
笛卡尔树
求 \(\sum\limits_{p \to T} \sum\limits_{u \in T} (rs_u-ls_u)\),其中 \(T\) 为笛卡尔树,且 \(u\) 必须同时拥有左右儿子才累和,\(ls_u,rs_u\) 为排列 \(p\) 中位置编号,\(n \le 10^6\)。
根据二叉搜索树的性质,根节点左右两边的位置编号应该是连续的。所以我们在累加 \(rs_u-ls_u\) 之后,便可以将问题转化为两个子问题了。
设 \(f_n\) 表示 \(n\) 个节点的答案,\(g_n\) 表示 \(n\) 个节点的时候,根节点权值之和。
\(g_n=\sum\limits_{i=1}^ni\times(n-1)!=\dfrac{n(n+1)}{2}\times(n-1)!=\dfrac{(n+1)!}{2}\)
设根节点在序列中的位置为 \(k\),那么左边节点数 \(a=k-1\),右边节点数 \(b=n-k\)。在分治前提计算之下左右两边分别是 \(1-a\) 和 \(1-b\) 的排列,最后加上根节点形成了一个 \(1-n\) 的排列,根节点是 \(1\),剩下就是要给两个排列分别分配权值,方案数是 \(\begin{pmatrix}a+b\\a\end{pmatrix}\)。
注意,只有同时具有左右儿子的时候才产生贡献,所以小心 \(k\) 的枚举上下界。同时注意我们其实是将右边的位置编号全体减小了 \(k\),所以虽然 \(f\) 是内部处理不影响,但是 \(g\) 是左右两边做差,故需要加上偏移量 \(k\),一共要加 \(a! \times b!\) 对,此时求解是 \(O(n^2)\)。
考虑将 \(\begin{pmatrix}a+b\\a\end{pmatrix}\) 写成阶乘形式,放进去,可以作前缀和,复杂度 \(O(n \log n)\)。
卢卡斯定理
公式及证明
- 公式
\(\begin{pmatrix} n\\m \end{pmatrix} \bmod p=\begin{pmatrix} \lfloor n/p\rfloor \\ \lfloor m/p \rfloor \end{pmatrix} \begin{pmatrix} n \bmod p \\ m \bmod p \end{pmatrix} \bmod p\) - 利用 \((a+b)^p\equiv a^p+b^p \bmod p\) 证明即可。
组合数奇偶性
\(\begin{pmatrix} n\\m \end{pmatrix}\) 为奇数,当且仅当 \(n \operatorname{and} m=m\)。
容斥
引入:有 \(k\) 种属性,你有一个函数 \(f(S)\),可以得知至少满足 \(S\) 属性的集合的物品个数。其中 \(f(\emptyset)=0\),求至少有一种属性的物品的个数。
容易得出,\(\sum\limits_{S \subseteq U}f(S)(-1)^{\lvert S\rvert+1}\)。
如何验证?假设一个物品有 \(n\) 个属性,在 \(n=0\) 的时候,\(f(\emptyset)=0\)。
如何 \(n \ge 1\),可以枚举一下哪些组合被枚举到。
\(\sum\limits_{i=1}^n\begin{pmatrix}n\\i\end{pmatrix}(-1)^{i+1}=-(\sum\limits_{i=1}^n\begin{pmatrix}n\\i\end{pmatrix}(-1)^i)=-((1-1)^n-1)=1\)。
所以对于 \(n \ge 1\) 的时候,每个物品恰好被枚举到了一次。
这种问题一般化解法,\(q(S)\) 表示可以快速得到至少满足 \(S\) 属性的物品的个数,还有一个贡献函数 \(g(k)\) 表示一个有 \(k\) 个属性的物品会对答案产生 \(g(k)\) 的贡献,本题中 \(g(0)=0\),其他 \(g(k)=1\)。同时还有容斥系数 \(f(\lvert S\rvert)\)。
我们可以通过 \(g(n)\) 来计算容斥系数。
可以通过递推 \(O(n^2)\) 来计算 \(f(x)\)。
引入2:有 \(n\) 个背面朝上的硬币编号为 \(1-n\)。有 \(m\) 次操作,每次给定 \(a_i\),把所有 \(a_i\) 的倍数的硬币都翻转。求每次操作后有多少个硬币正面朝上。
转化一下题意,就是问有多少个数被奇数个 \(a_i\) 整除。
考虑枚举集合 \(S\),通过计算 \(\operatorname{lcm}\) 可以得到 \(q(S)\)。
我们来计算一下容斥系数,\(g(n)=[n \bmod 2=1]=\sum\limits_{i=0}^k\begin{pmatrix}k\\i\end{pmatrix}f(i)\)。
可以 \(O(m^2)\) 递推。或者观察结果得出 \(f(n)=(-2)^{n-1}\)。
快速计算容斥系数
\(g(n)=\sum\limits_{i=0}^n\begin{pmatrix}n\\i\end{pmatrix}f(i)\)
可以得出,\(\dfrac{g(n)}{n!}=\sum\limits_{i=0}^n\dfrac1{(n-i)!}\dfrac{f(i)}{i!}\)。
设 \(G(x)=\sum\limits_{i=0}^{\infty}\dfrac{g(i)}{i!}x^i\),\(H(x)=\sum\limits_{i=0}^n\dfrac{1}{i!}x^i\),\(F(x)=\sum\limits_{i=0}^{\infty}\dfrac{f(i)}{i!}x^i\)。
则 \(G(x)=H(x)F(x)\),于是 \(F(x)=\dfrac{H(x)}{G(x)}\)。运用多项式求逆可在 \(O(n\log n)\) 的时间内求出。
子集反演
\(f(S)=\sum\limits_{T \subseteq S}g(T) \Leftrightarrow g(S)=\sum\limits_{T \subseteq S}(-1)^{\lvert S\rvert-\lvert T\rvert}f(T)\)
\(f(S)=\sum\limits_{S \subseteq T}g(T) \Leftrightarrow g(S)=\sum\limits_{S \subseteq T}(-1)^{\lvert S\rvert-\lvert T\rvert}f(T)\)
ABC294Ex K-Coloring
直接统计不太方便可以考虑使用容斥。可以画个韦恩图感受一下。就是总方案减去那些一个的部分,再加上两部分重叠的...数学上来说就是枚举边集 \(S\),假设只连这些边形成了 \(c\) 个联通块,那么贡献是 \(K^c \times (-1)^{\lvert S\rvert}\)。这样子的复杂度是 \(O(2^m(n+m))\)。似乎不太方便直接优化,我们发现当 \(c\) 不变的时候,\(\lvert S\rvert\) 相差一的时候会相互抵消。放到图上的意义就是并查集。考虑使用 dfs (可以剪枝,枚举量不满)代替枚举。如果有并查集内已联通的边就直接返回了。
二项式反演
形式一:\(f_n=\sum\limits_{i=0}^n\begin{pmatrix}n\\i\end{pmatrix}g_i\Leftrightarrow g_n=\sum\limits_{i=0}^n(-1)^{n-i}\begin{pmatrix}n\\i\end{pmatrix}f_i\)
形式二: \(f_i=\sum\limits_{j=i}^n\begin{pmatrix}j\\i\end{pmatrix}g_j \Leftrightarrow g_i=\sum\limits_{j=i}^n(-1)^{j-i}\begin{pmatrix}j\\i\end{pmatrix}f_j\)
对于形式二有组合解释,\(f_i\) 表示钦定选 \(i\) 个方案数,意思就是确定选 \(i\) 个特定的,然受剩下的随便选,由于钦定的不同所以 \(f\) 的方案有重复。\(g_i\) 表示恰好选 \(i\) 个。所以对于 \(i\ge n\),\(g_i\) 在 \(f_n\) 中被计算了 \(\begin{pmatrix}i\\n\end{pmatrix}\) 次。
应用:我们可以通过 DP 求出钦定选择 \(i\) 个元素的方案数,然后通过二项式反演得到恰好选 \(i\) 个的方案数。
错排问题 求 \(\sum_p[\forall i,p_i \neq i]\)。
设 \(g_n\) 表示 \(n\) 个人的错排方案数,而 \(f_n\) 表示所有排列的方案数。
枚举站错人的个数有, \(f_n=\sum\limits_{i=0}^n\begin{pmatrix}n\\i\end{pmatrix}g_i\)。
反演一下可以得到,\(g_n=\sum\limits_{i=0}^n(-1)^{n-i}\dfrac{n!}{(n-i)!}\)。
一维染色问题 有 \(n\) 个方格,\(k\) 种颜色,相邻格子颜色不同,每个颜色至少出现一次,求方案数。
设 \(g_k\) 表示出现 \(k\) 个颜色的方案数,\(f_k\) 表示 \(k\) 个颜色的总方案数。
\(f_k=k \times (k-1)^n\),并且 \(f_k=\sum\limits_{i=0}^k\begin{pmatrix}k\\i\end{pmatrix}g_i\)。
反演一下,\(g_k=\sum\limits_{i=0}^k(-1)^{k-i}\begin{pmatrix}k\\i\end{pmatrix}f_i\)。
二维染色问题 有 \(n\times m\) 个格子,每行每列都至少有一个格子被染色,求方案书。
设 \(g_{i,j}\) 表示恰好有 \(i\) 行 \(j\) 列被涂色的方案数,\(f_{i,j}\) 表示 \(i\) 行 \(j\) 列被涂色的方案数即 \(2^{ij}\)。有
\(f_{n,m}=\sum\limits_{i=0}^n\sum\limits_{j=0}^m\begin{pmatrix}n\\i\end{pmatrix}\begin{pmatrix}m\\j\end{pmatrix}g_{i,j}\)
反演一下,\(g_{n,m}=\sum\limits_{i=0}^n\sum\limits_{j=0}^m(-1)^{n-i+m-j}\begin{pmatrix}n\\i\end{pmatrix}\begin{pmatrix}m\\j\end{pmatrix}f_{i,j}\)。
三维染色问题 有 \(n \times m\) 个格子,\(k\) 种颜色,每行和每列至少一个格子被涂色,每个颜色至少出现一次,格子可不被涂色,求方案数。
\(f_{n,m,k}=\sum\limits_{i=0}^n\sum\limits_{j=0}^m\sum\limits_{p=0}^k\begin{pmatrix}n\\i\end{pmatrix}\begin{pmatrix}m\\j\end{pmatrix}\begin{pmatrix}k\\p\end{pmatrix}g_{i,j,p}\)
反演一下,\(g_{n,m,k}=\sum\limits_{i=0}^n\sum\limits_{j=0}^m\sum\limits_{p=0}^k(-1)^{n-i+m-j+k-p}\begin{pmatrix}n\\i\end{pmatrix}\begin{pmatrix}m\\j\end{pmatrix}\begin{pmatrix}k\\p\end{pmatrix}f_{i,j,p}\)。
斯特林数
第一类斯特林数
\(s(n,m)\) 表示 \(n\) 个不同的元素构成 \(m\) 个圆排列的个数。
递推公式: \(s(n,m)=s(n-1,m-1)+(n-1) \times s(n-1,m)\)
前者表示该元素单独形成一个圆排列,后者表示先生成 \(n-1\) 个元素的再将该元素插入 \(n-1\) 个元素前面的一个空,共 \(n-1\) 种插法。
\(\begin{bmatrix}n\\m\end{bmatrix}\)
子集卷积
高维前缀和
高维前缀和时间复杂度 \(O(\sum V_i \prod V_i)\)。
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
for(int k=1;k<=p;++k) a[i][j][k]+=a[i-1][j][k];
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
for(int k=1;k<=p;++k) a[i][j][k]+=a[i][j-1][k];
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
for(int k=1;k<=p;++k) a[i][j][k]+=a[i][j][k-1];
好处:对每一个维度单独求一遍前缀和即可,不需要写容斥原理了。
推广到集合角度:
for(int i=0;i<w;i++)
for(int j=0;j<(1<<w);j++)
if(j&(1<<i))s[j]+=s[j^(1<<i)];
[ARC100E] Or Plus Max
把二进制下标看成集合,or 相当于并集。就是一个对各子集合取最大和次大的操作。
CF165E Compatible Numbers
把二进制下标看成集合,所求的就是补集的子集。
CF1208F Bits And Pieces
这里有一个小技巧,\(x~or~y=x+\overline x~and~y\)。于是本题也可以这么转化。接着枚举 \(i\),就变成在 \(i<j<k\) 的前提下求 \(\overline a_i~and~a_j~and~a_k\) 的最大值。我们通过高维前缀和可以预处理 \(f_s\) 表示 \(s \in a_i\) 中的 \(i\) 的最大值与次大值。然后对于上面的表达式从高位到低位判断,加入处理到了第 \(z\) 位,那么对于当前已经满足要求的集合 \(S\) 先加上 \(2^z\),然后判断 \(f_s\) 中的次大值是否大于 \(i\)。如果大于表示满足,保留新的 \(s\) 继续询问。否则 \(s\) 减去 \(2^z\)。