组合基础
OI 中的组合,基本指组合计数。组合极值一般是贪心题或者 dp 题。
【组合数】
组合数 \(C^m_n=(^n_m)\)。
注意:求逆元前,请一定判断清楚,是否可能不存在逆元!!!
-
\(C^m_n = C^m_{n-1}+C^{m-1}_{n-1}\)。
c[n][m] = c[n - 1][m] + c[n - 1][m - 1];
这个方法主要问题在于空间。
优点:可以把
c[1 ~ n][1 ~ m]
全部求出来,好写。缺点:空间大。
复杂度:\(O(nm)\)。
适用情况:\(n,m\) 都小。
-
\(C^m_n=\frac{n\times (n-1)\times \cdots \times (n-m+1)}{m\times (m-1)\times \cdots \times 1}\)。
问题:我们不可能把分子乘完再除以分母,会爆的。
方法一:\(C^m_n = \displaystyle \prod_{i=1}^m \frac{n-i+1}{i}\)。 但是这个方法可能会爆。
方法二:求出 \(1\sim m\) 的逆元。然后把除换成乘。一路直接乘过去,边乘边模。
复杂度:算一次 \(O(m)\)。
适用情况:\(m\) 小。
-
\(C^m_n=\frac{n!}{(n-m)!m!}=n!(m!)^{-1}[(n-m)!]^{-1}\)。
只要能求出阶乘的逆元,就可以 \(O(1)\) 计算。
我们考虑 \((n!)^{-1}\) 和 \([(n+1)!]^{-1}\) 互相转化。
\(1\equiv (n+1)!\times [(n+1)!]^{-1} \equiv n!\times (n+1)[(n+1)!]^{-1}\)。
所以 \((n!)^{-1}\equiv [(n+1)!]^{-1}(n+1)\)。
我们先欧拉函数暴力计算 \(n!\) 的逆元,然后从大往小推出所有阶乘逆元。
注意:我们这里是可以从 \(n+1\) 推 \(n\),而不是 \(n\) 推 \(n+1\)。
优点:\(O(n)\) 预处理之后,每次询问 \(O(1)\)。
复杂度:\(O(n)\)。
适用场景:\(O(n)\) 可行,需要多次查询。
-
卢卡斯定理(Lucas)。
二项式定理:\((a+b)^n=\displaystyle \sum_{i=0}^n C^i_n a^{n-i}b^i\)。
引理:对 \(k:1\sim(p-1)\),\(p\) 为质数,\(p\mid C^k_p\)。
引理证明:\(C^k_p=\frac{p!}{k!(p-k)!}\),分子有一个 \(p\),分母没有 \(p\)。
卢卡斯定理:\(C^m_n\equiv C^{m/p}_{n/p}\times C^{m\%p}_{n\%p} \pmod p\)。
(若 \(C_n^m\) 中 \(m>n\),则 \(C_n^m=0\))
证明:
记 \(n=ap+b,m=kp+t\;(0\leq b,t < p)\)。
\((1+x)^n=C_n^0x^0+C_n^1x^1+\cdots+C_n^mx^m+\cdots+C_n^nx^n.\)
又 \((1+x)^n=(1+x)^{ap+b}=(1+x)^{p^a}\times (1+x)^b\;\;\;\;(1).\)
而 \((1+x)^p=C_p^0x^0+C_p^1x^1+\cdots+C_p^px^p\equiv C_p^0+C_p^px^p\)。
所以 \((1)\equiv (1+x^p)^a\times (1+x)^b\equiv [C_a^0(x^p)^0+C_a^1(x^p)^1+\cdots+C_a^k(x_p)^k+\cdots][\cdots+C_b^tx^t+\cdots].\)
发现 \(x\) 的 \(kp+t\) 次项,一定是从左边括号选出 \(kp\) 次项,右边括号选出 \(t\) 次项,然后相乘得出的。
所以 \(x\) 的 \(kp+t\) 次项的系数一定是 \(C_a^k\times C_b^t\)。
而从二项式定理的展开中知道,\(x\) 的 \(m=kp+t\) 次项的系数应该是 \(C_n^m\)。
所以 \(C_n^m=C_a^k\times C_b^t=C_{n/p}^{m/p}\times C_{n\%p}^{m\% p}\)。
证毕。
复杂度:\(O(p\log_p^{\;n})\)。
适用场景:\(n,m\) 非常大,\(O(p)\) 可行。或者 \(n,m\geq p\),不能直接求逆元(不能保证互质),用卢卡斯定理转化。
三角形个数 = 任取三个点的方法数 - 三点共线个数。
总数:\(C^3_{(n+1)(m+1)}\)
行的三点共线:\((n+1)C^3_{(m+1)}\)
列的三点共线:\((m+1)C^3_{(n+1)}\)
斜的:
考虑枚举所有点,对于一个点,枚举所有斜率,看这条线上有多少个点,然后用组合数求出来。
但是发现一个斜率可能有很多个点所得出的答案一样。
于是我们改一下顺序:先枚举所有斜率,然后对于所有可以放上这个斜率的点,求出这个斜率上所有点,然后再减去。
斜率可以看做一个长方形的对角线。于是我们枚举这个长方形的长 \(i\) 和宽 \(j\)。那么可以作为长方形左上角的点就有 \((n+1-i)(m+1-j)\) 个。
在每一个长 \(i\) 宽 \(j\) 的长方形对角线上,一共有 \(gcd(i,j)+1\) 个格点。(仪仗队)因此,取左上角、右下角和对角线中间 \(gcd(i,j)-1\) 个点中任意一个点都能组成一组三点共线,并且每一组之间不会重复。
另外,还要注意,长方形有两条对角线,所以每次减的时候要减两份。
其实就是卡特兰数。
考虑把问题对应成一个 \(m\times n\) 的方格表(\(n\) 是横着的)。最初我们在左下角,现在要去到右上角,每次可以向右或者向上移动一格。
把向右移动一格看做是在前面放一个 1,向上移动一格看做是放一个 0。那么移动到右上角就相当于我们取完了所有 0 和 1。每条移动的路径都和一个字符串一一对应。
条件 “任何前缀中 1 的个数必须不少于 0”,就相当于从左下角画一条 45° 向右上角走的斜线,不能越过这条斜线。
把这条斜线整体上移一个,条件就变成了不能碰到这条斜线。
我们还是考虑总数减去不符合要求的数。
总数:\(C^m_{(n+m)}\),即从 \(n+m\) 步中选 \(m\) 个位置向上走。
不符合方案(碰到斜线):
把这个方格表的左边加一列,并把斜线往左下角延伸一个。此时的斜线就连到了新方格表的左下角。
考虑所有不符合的方案,一定存在第一个点,碰到了斜线。
我们把这个点之前进行的所有步,都关于这条斜线做对称。
这样我们就得到了一条新路径:从新表左下角的上面一格出发,走到右上角。
我们发现,每一条这种路径,都与一条不符合要求的路径一一对应。并且,这种路径一共走了 \(n+m\) 步,其中有 \(m-1\) 步向上。
所以,这种路径的数量是 \(C^{(m-1)}_{(n+m)}\)。
因此,答案就是 \(C^m_{(n+m)} - C^{(m-1)}_{(n+m)}\)。
插板法。
\(k\) 种里面选 \(x\) 个。考虑第 \(i\) 种选了 \(x_i\) 个。就是解 \(x_1+x_2+\cdots+x_k=x\) 的非负整数解。
\(C_{x+k-1}^{k-1}\),也可以说是 \(C_{x+k-1}^x\)。
注意:\(n,n+1\) 之间没有要求。
两种情况:\(x,y\) 一左一右,或者 \(x,y\) 同侧。
-
一左一右。
假设高度 \(k\)。则 \(1\sim x-1\) 的楼高度取值 \(1\sim k\)。
我们发现,只要取出了 \(x-1\) 个高度,其排列方式已经固定了。
所以,这就是一个 “可重组合”。从 \(k\) 种里面选 \(x-1\) 个,答案 \(C^{x-1}_{k+x-2}\)
之后另外三个部分都差不多,求完四块之后全部相乘,就是 \(x,y\) 高度为 \(k\) 的方案数。
复杂度 \(O(n+m)\)。因为 \(O(n)\) 预处理组合数(第三种),\(O(m)\) 枚举 \(x,y\) 的高度。
-
同侧。
显然,同左和同右是对称的。
这种其实就是只有三个部分——\(x,y\) 中间夹着的一定高度都是 \(k\)。
【容斥原理】
有三个集合 \(A,B,C\)。现在想要知道 \(|A\cup B \cup C|\)。
\(|A\cup B\cup C|=|A|+|B|+|C|-|A\cap B|-|B\cap C|-|A\cap C|+|A\cap B\cap C|\)。
例如,\(1\sim n\) 中 \(2\) 或 \(3\) 或 \(5\) 的倍数有几个,就是 \(n/2+n/3+n/5-n/6-n/15-n/10+n/30\)。(下取整)
容斥原理:
\(|A_1\cup A_2\cup \cdots \cup A_n|=\displaystyle \sum |A_i|-\displaystyle \sum_{i\neq j} |A_i\cap A_j|+\sum |A_i\cap A_j\cap A_k|-\cdots.\)
证明方法考虑归纳法。
容斥原理的题目一般长这样:
符合条件 \(t_1,t_2,\cdots,t_n\) 至少一个的方法数。(1)
对 \(t_1,t_2,\cdots,t_n\) 的不符合的方法数。(2)
符合 \(t_i\) 的情况,记为 \(A_i\)。全集记为 \(S\)。
(1):答案为 \(A_1\cup A_2\cup \cdots \cup A_n\)。
(2):答案为 \(C_S(A_1\cup A_2\cup \cdots \cup A_n)\)。
而全集一般都很好求。所以情况(1)和情况(2)可以视为等价的。
不妨称 \(t_i\) 为 “基本条件”,\(A_i\) 为 “基本集合”。
容斥原理的代码:
写一个子集枚举,传参数记录选了多少个集合交起来。然后根据参数判断当前的答案是正是负。
记 \(p_i\) 为第 \(i\) 个质数,\(2^{50}\) 内有 \(k\) 个质数(\(k\) 大概是 \(3\times 10^7\))。\(A_i\) 为符合 “是 \(p_i^2\) 的倍数” 条件的集合。
则 \(A_1\cup A_2\cup \cdots\cup A_k\) 为所有 “非 squarefree” 的数。
\(|A_i|=[\displaystyle\frac{2^{50}}{p_i^2}]\),\(|A_i\cap A_j|=[\displaystyle\frac{2^{50}}{(p_ip_j)^2}]\),\(|A_i\cap A_j\cap A_t|=[\displaystyle \frac{2^{50}}{(p_ip_jp_t)^2}]\)……
这个并集是不好算的,但是交集很简单。
我们可以搜索——搜索出所有若干个质数相乘的所有次数为 \(1\) 的数,并同时记录每个数是由多少个质数乘起来的。(当然,质数乘起来必须 \(\leq 2^{25}\),否则平方就炸掉了。)
如果只有一次询问,可以直接多重背包加单调队列优化,但是有多次询问。
记 \(t_i\) 为 “第 \(i\) 种硬币使用超量”,\(A_i\) 为对应集合。
\(t_1\cup t_2\cup t_3\cup t_4\) 为 “至少有一种硬币超量”,是所有不符合条件的情况。
记 \(dp[x]\) 为凑 \(x\) 面值,每种硬币随便用的方法数。(完全背包)
\(|A_i\cap A_j|=dp[s - (d_i+1)c_i-(d_j+1)c_j]\),因为先把 \(i,j\) 两种取到保证超量。
因为硬币种数很少,所以每次询问求一次容斥都没压力。
一个东西选超量:先把它取完,再多取一个,最后随便选。
如果不要求每人至少拿一个,我们可以先把第一个分给所有人,然后分第二个…… 分每一个之间相互独立,分步计数。
如果要求每人至少一个,当前每个人是否拿到了就会影响后面的特产分发,不能分步计数。
但是,我们可以考虑反面:至少有一个人没有特产。
\(t_i\):第 \(i\) 个人没拿到特产。
想要的:\(A_1\cup A_2\cup \cdots \cup A_n\) 的补集。(所有同学都分到了)
\(|A_i|=\) 所有特产分给另外 \(n-1\) 个人的方法数。
\(|A_i\cap A_j|=\) 所有特产分给另外 \(n-2\) 人方法数 \(=\prod C_{a_i+(n-2-1)}^{a_i}\)。(隔板法)
另外注意到,无论是哪两个同学没分到,答案都相等。所以我们不需要真的枚举所有子集。
一个人没分到:把所有东西都分给别人。
分步考虑三个条件。
-
\(t_i\) 为第 \(i\) 种颜色不用。想要的是所有集合并起来的补集:所有颜色都用过。
\(|A_i\cap A_j|=\) 剩下 \(C-2\) 种颜色随便染的方法数。
复杂度贡献乘 \(C\),无论是选第 \(1,2\) 种颜色,还是选第 \(3,4\) 种颜色,答案都是一样的。
-
有 \(k\) 种颜色可选。\(t_i\) 为第 \(i\) 行无色。所有集合的并集的补集:每一行都有色。
\(|A_i\cap A_j|=n-2\) 行 \(m\) 列染色,不要求每行有色。
-
\(x\) 行 \(y\) 列 \(k\) 种颜色,只需每列有色。不考虑另外两个条件。
\([(k+1)^x-1]\) 是某一列有色的情况数:一列 \(x\) 个,每个有 \(k+1\) 种染色方式(包括不染),去掉每一个都不染的情况。
一共 \(y\) 列,\([(k+1)^x-1]^y\) 种方法。
用两次容斥,去掉两个条件。
//用x种颜色染色的方法,仍要求行列非全无色,但x种颜色不必都用过
long long cnt(long long x) {
long long ans = 0;
// 其中i行可以有颜色,即某n-i行无色
for (long long i = n, t = 1; i >= 1; i--, t *= -1)
ans = (ans + fpow((fpow(x + 1, i) - 1)/*一列全部方案减去一种全无色*/, m)/*所有列非全无色*/ * t * cmb(n, i) % p + p) % p;
//X种颜色不必都用过(但每行每列必须有色)=X种颜色涂n行(颜色不必都用每行不必有色但每列必须有色)-X种涂n-1行(颜色不必都用每行不必有色但每列必须有色) + X种颜色涂n-2行(颜色不必都用每行不必有色但每列必须有色)- ....
//X种颜色涂i行(颜色不必都用,每行不必有色,但每列必须有色)已经足够简单,方法数是 [(X+1)^i - 1]^m
return ans;
}
for (long long i = c, t = 1; i >= 1; i--, t *= -1)
ans = ((ans + cmb(c, i) * cnt(i) * t) % p + p) % p;
//ans = C种颜色不必都用过(但每行每列必须有色) - C-1种颜色不必都用过(但每行每列必须有色) + C-2种颜色不必都用过(但每行每列必须有色) - ....
\(f(n)=(n-1)(f(n-2)+f(n-1))\)。
考虑所有符合条件的路径,一定有 “第一个碰到斜线的地方”。(至少在终点碰到了)
\(ans=\) 从起点到这个地方 \(\times\) 从这个地方到终点。
这两个部分分别是更小的卡特兰数。
当出入栈序列固定,最终序列也就确定了。
出入栈序列只要求每个前缀中,出栈个数小于等于入栈。
这就是卡特兰数。
简化题意:
给出一个音阶集合 \(S=\{x_1,x_2,...,x_n\}\)。记其非空子集(片段)为 \(S_1,S_2,...,S_{2^n-1}\)。
要求选出 \(m\) 个非空子集 \(S_{t_1},S_{t_2},\cdots,S_{t_m}\),要求每个音阶在选出的非空子集中出现偶数次 且 \(t_1,t_2,\cdots,t_m\) 互不相同。
问有多少组 \(\{t_1,t_2,\cdots,t_m\}\)。
三个条件:不同、非空、\(x_i\) 出现偶数次。
设 \(dp[i]\) 为选 \(t_1\sim t_i\) 并符合条件的方法数。
分步。
-
任选 \(t_1\sim t_{i-1}\),只要求不同非空即可,\(\displaystyle C_{2^n-1}^{\,i-1}\)。
-
选 \(t_i\)。对于一个音阶 \(x_a\),如果在选出的 \(t_1\sim t_{i-1}\) 中出现奇数次,必须选;否则必须不选。
考虑特殊情况:\(t_i\) 和之前的重复了,或者是空集。
-
\(S_{t_i}\) 空,意味着前 \(i-1\) 个已经满足了所有条件。
因为 “\(i\) 个有一个空集” 和 “\(i-1\) 个刚好满足所有条件” 一一对应,所以一共有 \(dp[i-1]\) 个这种情况。
-
\(S_{t_i}\) 和之前的重复了(显然这种情况和 1 类没有重复),意味着把 \(S_{t_i}\) 和与它重复的集合去掉,又可以满足所有条件。
考虑这个对应关系。
显然 \(i\) 个片段含一对重复,可以唯一对应到 \(dp[i-2]\) 里面。
但是 \(dp[i-2]\) 对应到很多个 “\(i\) 个含一对”,因为 \(i-2\) 含的一对相同片段有 \([(2^n-1)-(i-2)]\) 种选择。
所以最后有 \(dp[i-2]\times [(2^n-1)-(i-2)]\) 种这个情况。
-