组合基础

OI 中的组合,基本指组合计数。组合极值一般是贪心题或者 dp 题。

【组合数】

组合数 \(C^m_n=(^n_m)\)

注意:求逆元前,请一定判断清楚,是否可能不存在逆元!!!

  1. \(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\) 都小。

  2. \(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\) 小。

  3. \(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\)

    code

    优点:\(O(n)\) 预处理之后,每次询问 \(O(1)\)

    复杂度:\(O(n)\)

    适用场景:\(O(n)\) 可行,需要多次查询。

  4. 卢卡斯定理(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\) 个点中任意一个点都能组成一组三点共线,并且每一组之间不会重复。

另外,还要注意,长方形有两条对角线,所以每次减的时候要减两份。

code

生成字符串

其实就是卡特兰数。

考虑把问题对应成一个 \(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)}\)

code

可重组合

插板法。

\(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\) 同侧。

  1. 一左一右。

    假设高度 \(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\) 的高度。

  2. 同侧。

    显然,同左和同右是对称的。

    这种其实就是只有三个部分——\(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}\),否则平方就炸掉了。)

code

硬币购物

如果只有一次询问,可以直接多重背包加单调队列优化,但是有多次询问。

\(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}\)。(隔板法)

另外注意到,无论是哪两个同学没分到,答案都相等。所以我们不需要真的枚举所有子集。

一个人没分到:把所有东西都分给别人。

染色问题

分步考虑三个条件。

  1. \(t_i\) 为第 \(i\) 种颜色不用。想要的是所有集合并起来的补集:所有颜色都用过。

    \(|A_i\cap A_j|=\) 剩下 \(C-2\) 种颜色随便染的方法数。

    复杂度贡献乘 \(C\),无论是选第 \(1,2\) 种颜色,还是选第 \(3,4\) 种颜色,答案都是一样的。

  2. \(k\) 种颜色可选。\(t_i\) 为第 \(i\) 行无色。所有集合的并集的补集:每一行都有色。

    \(|A_i\cap A_j|=n-2\)\(m\) 列染色,不要求每行有色。

  3. \(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\) 并符合条件的方法数。

分步。

  1. 任选 \(t_1\sim t_{i-1}\),只要求不同非空即可,\(\displaystyle C_{2^n-1}^{\,i-1}\)

  2. \(t_i\)。对于一个音阶 \(x_a\),如果在选出的 \(t_1\sim t_{i-1}\) 中出现奇数次,必须选;否则必须不选。

    考虑特殊情况:\(t_i\) 和之前的重复了,或者是空集。

    1. \(S_{t_i}\) 空,意味着前 \(i-1\) 个已经满足了所有条件。

      因为 “\(i\) 个有一个空集” 和 “\(i-1\) 个刚好满足所有条件” 一一对应,所以一共有 \(dp[i-1]\) 个这种情况。

    2. \(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)]\) 种这个情况。

posted @ 2024-02-15 11:16  FLY_lai  阅读(5)  评论(0编辑  收藏  举报