本文搬运自本人初中博客园博客,若图片加载不出来,可到原文查看:https://www.cnblogs.com/zhangtingxi/articles/15679108.html
题单
容斥和组合数
自我介绍
吕欣。活跃在 2016 - 2019 年的一个 OIer。参加过 NOI 2015 (铁牌),NOI 2016 (金牌)。2017 年进入清华大学姚班读书。在 2017 - 2019 年给各种 OI 比赛出过一些题(清华集训 2017 生成树计数;NOIWC 2019 I君的商店;NOI 2019 I君的探险;CTSC 2019 氪金手游),对(当时的)命题风格/环境/人有一定的了解。
邮箱:lyuxin1999@gmail.com
容斥好题
题1. (清华集训 主旋律)给定一个 N 个点的有向图,问这个图有多少个强连通子图。保证 N≤15。
(也可以尝试去做完全图的情况:N 个点的强连通图个数。)
解法
(图里可能有 N2 条边)
简单问题:给定一个有向图,问有多少个子图是 DAG。
DP: f[S] 表示在点集 S 之间删除一些边,使得剩下的图是个DAG的方案数。枚举哪些点的入度为 0,然后递归。
f[S]=T⊆S,T=∅∑2#edges(T,S−T)⋅f[S−T].
但是这个递归式会算重:枚举的时候只保证了 T 里的点入读为 0,但是 S−T 可能也有入度为0的点。使用容斥:
f[S]=T⊆S,T=∅∑2#edges(T,S−T)⋅f[S−T]⋅(−1)∣T∣−1.
使用这个DP计算,计算量是 3N。
原题
一个图如果不强连通的话,那它就可以写成大于 1 个强连通分量连成的一个DAG。
强连通图的个数 = 所有图的个数 - 非强连通的个数。
尝试计算非强连通图的个数,枚举一个 T,让 T 连成一个强连通图,T 不接受来自 S−T 的边。
f[S]=ALL(S)−T⊆S,T=∅∑f[T]⋅2ways(T,S−T)⋅ALL(S−T).
ALL(S) 表示所有可能的图的个数。
令 g[T] 表示:
- 把集合 T 划分成若干块,每一块里连成一个强连通块。如果有奇数个块:产生 1 的贡献;如果有偶数个块:产生 -1 的贡献。
- g[T] 是所有的贡献之和。
f[S]=ALL(S)−T⊊S,T=∅∑g[T]⋅2ways(T,S−T)⋅ALL(S−T).
怎么算 g?给定一个集合 S,固定 x∈S,枚举 x 所在的强连通块:
g[S]=f[S]−T⊆S,x∈T∑f[T]⋅g[S−T]
题2. (ZJOI 小星星)给定一个 n 个点 m 条边的无向图和一个 n 个点的树。问把这个树“嵌入”到这个图里的方案数。保证 n≤17。
做法 1:在树上DP,写 f[x][S][y] 表示处理了 x 为根的子树,图里的S这些点已经被用了,x 被映射到了图里 y 这个点上。
f[x][S][y]⋅f[ch[x]][S′][y′]⋅1[(y,y′)∈G]→f′[x][S∪S′][y].
直接做是 O(3n⋅n3)。可以用 FMT(快速莫比乌斯变换) 优化到 O(2n⋅n3).
做法 2:容斥。如果不考虑“每个图里的点都要用”,写一个DP:f[x][y] 表示 x 为根处理完,x 映射到 y 上的方案数。
这样做的话可能图里有些点没有被用到。枚举哪些点没有被用到,容斥。 O(2n⋅n3)。
题3. (SHOI 黑暗前的幻想乡)给定一个 n 个点的图。每条边被染上了 n−1 种颜色里的一种。问有多少个生成树包含了 n−1 个不同颜色的边。n≤17。 (author:陈立杰)
如果没有颜色的限制:Matrix Tree 定理,O(n3)。
有颜色限制:枚举哪些颜色没有被用到,做容斥。O(2nn3)。
题4. (随机 K 大值),现在有 N 个独立的随机变量,第 i 个变量 Xi 服从 [Li,Ri] 上的均匀分布。问 X1,…,XN 中的第 K 大的数的期望。N≤50,Li,Ri≤100。
考虑简单版本:Li=0,Ri=100 (从 [0, 100] 里均匀独立地随机 N 个数,问其中第 K 大的期望是多少。)
通常的容斥:有 N 个限制条件,问有多少种方案 (1) 满足至少一个限制条件 / (2) 不满足任何限制条件。
如果我们问 (3) 有多少种方案满足恰好 K 个限制条件? (3‘)有多少方案满足至少 K 个条件。
用 f[S] 表示满足并且只满足集合 S 里的限制条件的方案数。用 g[S] 表示至少满足了集合 S 里的限制条件的方案数。
g[S]=S⊆T∑f[T].
由反演公式:
f[T]=T⊆S∑g[S]⋅(−1)∣S−T∣.
那么问题 1 的答案是:
S=∅∑f[S]=ALL−f[∅]=ALL−S∑g[S]⋅(−1)∣S∣=S=∅∑g[S]⋅(−1)∣S∣−1.
这里是因为 g[∅]=ALL。
问题 (3):
∣S∣=K∑f[S]=∣S∣=K∑S⊆T∑g[T]⋅(−1)∣T−S∣=∣S∣=K∑g[T]⋅S⊆T,∣S∣=K∑(−1)∣T−S∣=∣S∣=K∑g[T]⋅(K∣S∣)⋅(−1)∣S∣−K
问题 (3’):
∣S∣≥K∑f[S]=∣S∣≥K∑S⊆T∑g[T]⋅(−1)∣T−S∣=∣T∣≥K∑g[T]∣S∣≥K,S⊆T∑(−1)T−S=∣T∣≥K∑g[T]⋅A(∣T∣,K).
其中 A(n,k)=∑i=kn(in)⋅(−1)n−i.
(作为一个特殊情形,考虑 k=1 的时候,∑i=1n(−1)n−i(in)=(−1)n+1,此时得到奇偶错位求和)。
ANS=∫0RPr[第 K 大 数≥x]dx=∫0RPr[∃K个数≥x]dx=∫0R(∣S∣≥K∑f[S])dx=∫0R(∣S∣≥K∑g[S]⋅c∣S∣)dx
其中 f[S] 表示 Xi≥x 对任意 i∈S 成立,并且 Xi<x 对任意的 i∈/S 成立。
g[S] 表示 Xi≥x 对任意 i∈S 成立。c∣S∣ 是一个容斥系数。(这个概率就是 ∏i∈SRR−x,对 x 积分即可)。
当 Li,Ri 不一定一样的时候:把 [0,+∞) 按照 Li,Ri 分段,每一段用上述技巧计算多项式,求积分。(ABC:Random K-th Max)
题5. (BZOJ 已经没有什么好害怕的了) 给定两个长度为 N 的数组 {Ai} 和 {Bi},现在要把他们配对起来,使得恰好有 K 对 (A,B) 满足 A≥B,求方案数。N≤2000。
考虑一个暴力容斥:
枚举集合 A 里的 t 个数构成的集合 S,要求他们必须匹配到比自己小的,其余的N−t 个数随意,求方案数。
暴力枚举完所有可能情况,可以算出 g[t]:表示在集合A里钦定了某 t 个数,保证它们的匹配对象比自己要小,其他随意的方案数。
用 g[t] 反演出 f[t]:恰好有 t个A中的数,满足他们的匹配对象比自己小。答案就是 f[K].
优化:用一个DP来计算 g。令 dp[i][j] 表示在集合 A 的前 i 个数里,钦定了 j 个数的匹配对象比自身小,的方案数之和。
dp[i][j]→dp[i+1][j]dp[i][j]→×([比Ai+1小的数的个数]−j)→dp[i+1][j+1].
dp[N][t]⋅(N−t)!=g[t]。
运算量是 O(N2)
题6. (Atcoder Beginner Contest-???) 给定一个 1∼N 的排列 P。问有多少 i=j 满足 gcd(i,j)>1 且 gcd(Pi,Pj)>1。 N≤2×105。
简单问题:有多少 (i,j) 满足 gcd(Pi,Pj)>1(不考虑 gcd(i,j)>1 的限制)。容斥(莫比乌斯反演)
- 计算有多少 (i,j) 满足 $2 | (P_i,P_j) $:加上。
- 计算多少满足 3∣(Pi,Pj):加上。
- 计算有多少 6∣(Pi,Pj):减掉。
- 。。。
- 计算有多少 d∣(Pi,Pj):产生 −μ(d) 的贡献。
ANS=d=1∑Nμ(d)⋅(2d的倍数的个数)
可以实现一个数据结构,支持插入一个数,删除一个数;询问集合里有多少个互质的pair。
- 每次插入/删除一个数 x 的时候,枚举 x 的约数 d,更新 d 的倍数的个数。同时更新 ANS。
- 每次对数据结构的操作时间是2ω(x)≤N,其中 ω(x)表示 x 不同的质因子的个数。
原题:现在带上了 i j 的限制。
- 只考虑 2∣(i,j) 的那些下标,计算一遍简单问题。产生 1 的贡献
- 枚举 3∣(i,j) 的下标,计算一遍简单问题。产生 1 的贡献
- 。。。
- 枚举 d∣(i,j) 的下标,计算一遍简单问题。产生 −μ(d) 的贡献。
总共会对数据结构进行 O(NlogN) 次更新。
计算量:O(NNlogN).
组合数
定义:(KN)=K!(N−K)!N! 为从 N 个物品中取出 K 个的方案数。
常用公式
-
∑i=0N(iN)=2N.
-
∑i=0N(iN)(−1)i=0.
-
∑i=0N(iN)xi=(1+x)N.
-
∑i=0K(iN)(K−iM)=(KN+M). 范德蒙公式 ∑i=0N(iN)(iM)=∑i(iN)(M−iM)=(MN+M)
-
(KN)(iK)=(iN)(K−iN−i). 例子 ∑kf(i)∑i(KN)(iK)=∑i(iN)f(i)∑K(K−iN−i)=∑if(i)(iN)2N−i.
-
(iN)=(i−1N−1)+(iN−1)=(i−1N−1)+(i−1N−2)+(iN−2)=...=∑t=i−1N−1(i−1N−1).
-
(K+1N+1)=∑i=KN(Ki) (想象 N+1 个物品里选 K+1 个,我枚举第一个被选中的物品是第几个)。
- 等价地,(K+1N+1)=∑i=KN(i−Ki).
-
∑i=0K(iN+1)=∑i(iN)+(i−1N)=2∑i=0K(iN)−(KN).
-
Lucas 定理(SHOI 超能粒子炮;(例题2)[https://nanti.jisuanke.com/t/A1245])
题1. (LOJ 博弈论与概率统计)共有 T 组询问,每次给定 N,K,计算 ∑i=0K(iN),保证 K,N≤105,T≤105。
令 f(N,K) 表示答案。那么 f(N,K)=f(N−1,K)⋅2−(KN),f(N,K)=f(N,K−1)+(KN)。
当我们知道 f(N,K),可以在 O(1) 时间内算出 f(N+1,K) 和 f(N,K+1)。
莫队,O(T⋅N+M).
题2. (Atcoder BBQ Hard) 给定正整数数组 (ai)i=1N 和 (bj)j=1N,计算 ∑i=j(ai+ajai+aj+bi+bj),保证 N≤105,ai,bj≤2000。
相当于从 (−ai,−bi) 走到 (aj,bj),每次向右或者向上一步的方案数。对 i、j 求和相当于需要自选起点 i 和终点 j。做一个DP。
题3. 有一个 N×N 的网格图,其中有 K 个障碍点。计算从 (1,1) 移动到 (N,N),每次向右或者下走一步,中间不经过任何障碍点的方案数。保证 N≤105,K≤2000。
Skipped.
加强版:从 (1,1,1) 移动到 (N,M,K),每次选择一个维度走。
形如 (i,i,i) 以及 (N−i,M−i,K−i) 的点都是障碍点。求方案数。 N,M,K≤105。
FFT + 生成函数 (多项式求逆)。
Skipped.
题4. (Atcoder - ARC118E) 有一个 (N+2)×(N+2) 的网格图。对于一个 N 元排列 P,我们把 {(i,Pi)} 标记为 N 个障碍点,然后求从 (0,0) 移动到 (N+1,N+1) 的,不经过障碍的路径方案数。对于所有可能的排列 P,求不经过障碍的方案数的总和。 N≤100.
原题:排列的一部分已经填好,只需要填剩下的位置。
如果排列已经给定的话:容斥:f[x][y] 表示走到 x、y 这个位置, 经过并标记了偶数个障碍的方案数 - 经过并标记了奇数个障碍方案数。f[x][y]→f[x+1][y]/f[x][y+1]。
排列没有给定的话:把生成排列的过程也放进DP 里. f[x][y][k][flag] 表示在 x、y 这个位置,排列里确定了 k 个元素。flag:当前行 / 列是否已经放了一个障碍。经过并标记了偶数个障碍的方案数 - 经过并标记了奇数个障碍方案数。
枚举 (1) 在当前位置是否放障碍 /(2)向那个方向走。 O(N3)。
- 进入一个新的行/列:可以知道当前行列是否已经有障碍。
- 因为有 flag,所以我们放的障碍一定保证不同行不同列。
- 我们记录了 k,我们放的障碍个数。这相当于说把排列里 K 个位置填好了。DP结束的时候乘 (N−k)! 来保证生成的是一个排列。
题5. (AGE - Yes or No) 从一个 N×M 的网格图的 (0,0) 移动到 (N,M),经过一个点 (i,j) 的时候可以获得 i+jmax(i,j) 的得分。求所有可能的路径的得分之和。N,M≤106。
i,j∑(ii+j)(N−iN−i+M−j)i+jmax(i,j)=i∑i!(N−i)!1j∑j!(M−j)!(i+j)!⋅(N+M−i−j)!i+ji=i∑i!(N−i)!1j∑
正常做法:从小到大枚举 i+j=s,想计算
i∑(is)(N−iN+M−s)smax(i,j)
我们分别计算
Ai(s)=s1i∑(is)(N−iN+M−s)⋅i⋅[i≥s/2].
和
Aj(s)=i∑(is)(N−iN+M−s)ij⋅[i<s/2]
考虑从 s 转移到 s+1:
Ai(s+1)=s+11i∑(is+1)(N−iN+M−s−1)⋅i⋅[i≥(s+1)/2].
(s+1)Ai(s+1)−sAi(s)=i∑((is)+(i−1s))(N−iN+M−s−1)⋅i⋅[i≥(s+1)/2]−i∑(is)((N−iN+M−s−1)+(N−i−1N+M−s−1))⋅i⋅[i≥s/2]
这里注意到 (is)(N−iN+M−s−1) 可以和 (is)(N−iN+M−s−1) 消掉。(i−1s)(N−iN+M−s−1) 可以错位和 (is)(N−i−1N+M−s−1) 消掉。因为只需要特殊计算 i≈(s+1)/2 的 O(1) 项即可。
(可以做 O(1) 递推)。
神棍做法:不妨设 N≥M。
i+jmax(i,j) 相当于说:还有 i 个 “yes” 和 j 个 “no” 在我的前面。我不知道他们出现的顺序,但是我想猜下一个位置是 yes 或者 no。我应该猜个数比较多的,我的成功率就是 i+jmax(i,j).
原问题相当于说,从 i,j=(n,m) 开始,每次猜下一个是 yes 或者 no,猜对了得分,猜错了不得分。问期望的总得分。
如果总是 i≥j 的话:我会一直猜 Yes,最后n+m轮里有 n 轮猜对,得 n 分。
如果 i=j :
- 如果猜错了:(i,j)=>(i,j−1) 没有得分,但是仍然有i≥j。
- 如果猜对了:(i,j)=>(i−1,j),这个局面和 (i,j−1) 是等价的。所以这里相当于我"白赚"了0.5分。
所以答案是
N+2⋅(NN+M)∑i=0N(i2i)(N−iN+M−2i).
题6. 组合数取模:计算 (MN)modP。
- 版本 1:P=109+7,N,M≤109.
- 版本 2:P 是一个小质数 (Lucas 定理).
- 版本 3:P=310,N,M≤1018.
- 版本 3 加强版:P=320,N,M≤1018.
基于拉格朗日插值的多项式求和
拉格朗日插值. 假设 P 是一个度数为 D 的多项式。如果我们知道 P 在 D+1 个不同位置的取值 (x1,P(x1)),…,(xD,P(xD)),那么我们可以唯一地把 P 写为
P(x)=i=1∑D+1P(xi)∏j=i(xi−xj)∏j=i(x−xj).
多项式的部分和. 假设 P:N→R 可以表示为一个度数为 D 的多项式,那么我们考虑函数 Q:N→R
Q(n)=i≤n∑P(i).
可以证明 Q 可以表示为一个度数为 D+1 的多项式,并且这个表示方法是唯一的。
如果已知 Q 在 0,1,…,D 这些输入上的输出,那么对于任意给定的数 n,可以在 O(D) 时间内计算出 Q(n)。算法:按照拉格朗日插值公式,只需要对每个 i∈{0,…,D} 计算 ∏j=i(n−j) 和 ∏j=i(i−j)。对于前者,我们只需要计算一个数列 (n−0),(n−1),…,(n−D),然后取一个前缀积和一个后缀积即可。对于后者,可以发现它就是 (i−1)!⋅(D−i)!⋅(−1)D−i。因此,整个计算可以在 O(D) 时间内完成。
题1. 给定 N,K,计算 ∑i=0NiK. 保证 K≤105.
题1 加强版. 给定 N,K,q,计算 ∑i=0NiKqi. 保证 K≤105.