【笔记】计数问题选讲 2025.1.6
笔记 2025.1.6:计数问题选讲-徐哲晨
- 笔记 2025.1.6:计数问题选讲-徐哲晨
- P4463 [集训队互测 2012] calc(拉插优化 dp)
- P4484 [BJWC2018] 最长上升子序列(状压 dp)
- ARC138E - Decreasing Subsequence(构造双射)
- P5400 [CTS2019] 随机立方体(二项式反演)
- AGC064D - Red and Blue Chips(构造充要条件)
- CF1942G. Bessie and Cards(反射容斥)
- CF1874F. Jellyfish and OEIS(容斥、构造双射)
- P8478 「GLR-R3」清明(乘法分配律)
- P4931 [MtOI2018] 情侣?给我烧了!(加强版)(错排)
- qoj 5357. 芒果冰加了空气(刻画)
- P10104 [GDKOI2023 提高组] 异或图(数位 dp、容斥)
- Baekjoon 23510 Wise man(数位 dp)
- (skipped)P5417 [CTSC2016] 萨菲克斯·阿瑞
- CF1456E XOR-ranges(数位 dp、独立性)
- (skipped)CF1081G Mergesort Strikes Back
- CF1707D Partial Virtual Tree
- (未完成)CF1647F Madoka and Laziness
- CF1349F1 Slime and Sequences (Easy Version)(构造映射、欧拉数)
- P5359 [SDOI2019] 染色
- (未完成)P5320 [BJOI2019] 勘破神机
- P9108 [PA2020] Malowanie płotu
- P10681 [COTS 2024] 奇偶矩阵 Tablica
P4463 [集训队互测 2012] calc(拉插优化 dp)
先简单地写一个二维的 dp 方程。然后发现因为所涉及的操作只有平移、前缀和、点乘一次函数的操作,我们将 dp 的某一行看成一个代数式 \(f(x)\),使得这行的第 \(x\) 个数刚好是 \(f(x)\)(注意不是第 \(x\) 项系数),然后我们就能直接证明 \(f(x)\) 是多项式!然后拉格朗日插值即可。
P4484 [BJWC2018] 最长上升子序列(状压 dp)
有两种求 LIS 的方法,应该选哪一种?如果选择 \(O(n\log n)\) 的方法,则无法在 \(\widetilde{O}(2^n)\) 的状态内同步记下选过哪些数,会较为失败。使用 \(O(n^2)\) 的方法,如果我们从排列出发,从左往右扫排列的第 \(i\) 个数,我们也需要记下选过哪些数,还要记下整个 dp 数组。
发现瓶颈其实在于要记下选过的数,这个东西记完之后就没有什么额外空间进行操作了。所以我们改从值域考虑这个问题,也有两个角度,第一种是从左往右扫,每次从值域中间、序列末尾插入;第二种是从下往上扫,每次从值域末尾、序列中间插入。做到这里两个角度已经没有什么区别,你对 dp 数组取前缀 max 再差分就会发现其值域变为 \(\{0, 1\}\),就可以存储了。然后再简单地计算一下复杂度就是 \(O(n2^n)\),接下来的事情就是打表了。
ARC138E - Decreasing Subsequence(构造双射)
当你终于将题目读对的时候会发现这个限制相当自由,如果只是从阶梯的形状下手会极为失败。对此,由题解知,我们将 \(i\) 向 \(a_i-1\) 连边,这样每个点要么连 \(-1\)(就是抛弃掉它)要么连向 \([0, i)\) 的其中一个点,每个点的入度和出度都为 \(1\),形成若干条链。最后就是发现下降子序列的形状是 \(k\) 条层层包含的边,枚举这个变所在的链,可以从那 \(k\) 条边中间将 \(k\) 条链拆开。然后使用第二类斯特林数(斯特林子集数)对左右两边的点进行划分链。注意这个映射构造的时候多了一个 \(0\) 号点,是原来的问题中不存在的。
P5400 [CTS2019] 随机立方体(二项式反演)
可能的第一个想法是:立方体中的最大值是极大点,将其提取出并删除它所在的三个方向的平面。然后选择下一个极大点,就会发现刚才删去的东西会对下一个有一点影响。那么我们应该从小到大枚举极大点吗?有可能,可以尝试一下,发现这样做就舒服很多。后面的事情就是,把题改成方案数,然后写出式子,消去若干阶乘。最后只需要惊讶地发现刚才做的事情只能保证有至少 \(k\) 个极大点(或者说“钦定”了 \(k\) 个极大点),于是快速地默写二项式反演的式子即可通过。
AGC064D - Red and Blue Chips(构造充要条件)
计数多少个本质不同的答案序列,太难了!有一个较为通用的套路是考虑怎么判定答案是否可以被构造。
写一下这题的过程,你从右往左扫描原串,扫到 \(R\) 就要有一个靠前的 \(R\) 脱落,扫到 \(B\) 就要大概是一个 \(B\) 带着它前面的东西脱落。有将 \(B\) 脱落这个过程,我们再重写一下,现在有若干个 \(B\) 结尾的串和需要单独分离的答案串的极长 \(R\) 前缀,扫到 \(R\) 就要有一个串的第一个 \(R\) 脱落,扫到 \(B\) 就要有一个 \(B\) 结尾的串分裂成两个 \(B\) 结尾的串,最后问是不是每个原串 \(R\) 都能找到脱落的答案串。那就很好办了,发现将 \(B\) 结尾的串分裂时,会多出来一个 \(R\) 的极长连续段可以进行脱落,而且是任意一个段都能脱落出来,那我肯定是贪心地选最长的那一个。
现在问题就变成了:在原串上从右到左统计每个 \(B\) 前面的 \(R\) 极长连续段长度记作 \(a\),在答案串从左到右统计每个 \(B\) 前面的 \(R\) 极长连续段长度记作 \(b\),然后排序 \(b[2\cdots len(b)]\),并要求 \(len(a)=len(b)\),\(a\) 做前缀和后的数组对应位都小于等于 \(b\) 做前缀和后的数组。可以直接 dp。
CF1942G. Bessie and Cards(反射容斥)
可能是转格路计数后做反射容斥,而事实就是这样,因为确定起点、终点、长度后,走过的 \(+1\) 和 \(-1\) 的步数是完全确定的,不会随着什么东西的变化而变化。所以就直接做,首先将抽 \(k\) 次改为 \(+k-1\) 牌,删掉 \(+0\) 牌,枚举抽了几张牌之后停止,或者没有停止过,那么所用的 \(+1\) 牌和 \(-1\) 牌的数量是完全确定的,什么都不用担心了。
CF1874F. Jellyfish and OEIS(容斥、构造双射)
上来就容斥,枚举一个区间的集合表示这些区间满足题目那个鬼限制。然后可以发现,两个有交的区间(\(l_1<l_2<r_1<r_2\)),可以通过额外加一个它们的交对应的区间,将它们拆成三个无交的区间,拆完就可以区间 dp 了。进一步的,如果有交,我们可以通过上述的操作,外加一些讨论,将其转化为一个方案数不变但是容斥系数取相反数的情况,于是根本就不用考虑区间有交的情况。
P8478 「GLR-R3」清明(乘法分配律)
题目大概能表述为,确定具体流水方案后,求一堆多项式的乘积。直接施乘法分配律,变成确定方案后每个位置还要选一个雨水,再独立成确定某个阶梯的水流到某个子集的雨水的值的乘积与没流到子集中的雨水的方案数的乘积。使用生成函数计算可得,或者使用 组合意义 推导可得一个组合数。
然后就交给状压 dp 了。根号分治,\(k\) 小的时候记这个阶梯的后 \(k\) 个的情况,\(k\) 大的时候记 \([k+1, n]\) 阶梯的情况,\([1, k]\) 的只记选过的数量。然后写一些高维前缀和再优化一下常数即可通过,复杂度有一堆 \(n\) 因子都没有关系。
P4931 [MtOI2018] 情侣?给我烧了!(加强版)(错排)
直接推导 二项式反演 比较简单,然后发现只能过简单版。所以你考虑使用 NTT 优化,发现过不去,所以只能弃暗投明,使用 Elegia 法 或者别的什么东西可以推导出二项式反演对应的系数,也就是错排公式。
错排,还是太有用,直接省略容斥部分。
qoj 5357. 芒果冰加了空气(刻画)
你发现两个连通块对应的点分树合并时,假如中间那条边是 \((u, v)\),则一旦分治中心再次选到 \(u, v\),之后就完全确定了。所以只有 \(u, v\) 在原来连通块上的深度是重要的,记下来之后直接 dp。真是太神奇了。
P10104 [GDKOI2023 提高组] 异或图(数位 dp、容斥)
你发现这题 \(m=0\) 都挺难做,所以 \(m=0\) 应该怎么做?你想做数位 dp,如果你继续想一下,可以发信啊,一旦有一个数不顶上界了,就可以让其他数随便填,然后这个数下面的位根据实际情况调整。而没有顶上界之前,决策都是较为固定的,所以可以枚举在哪一位开始有人不顶上界,在那一位上做 \(O(n)\) 的小 dp,总复杂度 \(O(n\log C)\)。
\(m>0\) 时,直接施容斥吧,枚举边集的一个子集,要求里面的数全部相同,就相当于划分出很多连通块(每个连通块有一个系数,表示能是连通块连通的边集的容斥系数之和,此后全都改为对点集划分,这部分随便做),删掉偶数大小连通块,奇数大小只保留最小值。那么可以 \(\widetilde O(4^n)\) 做这个鬼东西,瓶颈是无法区分保留的最小值和已经选的。不妨对 \(a_i\) 排序,dp 到 \(i\) 的时候,记录前 \(i\) 个是否是最小值以及后 \(n-i\) 个是否选择过,这样就不会混淆,用 \(\widetilde O(2^n)\) 的空间存下所有状态,转移枚举子集所以时间复杂度 \(O(3^nn+2^nn\log C)\)。
Baekjoon 23510 Wise man(数位 dp)
你首先发现这个题在 QOJ 没有,很生气,然后也不太会做。肯定有一个想法是从低到高地去顶 \(M\),但是状态有点多,和暴力一样,我们需要一个更有针对性的状态,要求它不能有太多不确定因素,然后需要能方便的支持跳转到对某一位进行 \(+1\) 后的情况。
\(dp[j][i][x]\) 表示当前这个数的个位(第 \(0\) 位)为 \(x\),接下来 \([1, i]\) 位全零,\((i, +\infty]\) 的数位最大值为 \(j\),对这样的数字使 \(i+1\) 位 \(+1\) 需要的步数以及 \(x\) 会变成什么样(发现这足够了,因为每次只会加 \([0, 9]\))。可以轻易转移。可以轻易使用这个结果进行操作。
有一个循环的问题,你发现发生 \(\geq M\) 的操作去 \(-M\) 时,只会到达 \([0, 8]\) 的 \(A\)。多次 \(-M\) 的过程中,可以找出那个环,快速跳跃。于是可以通过。
(skipped)P5417 [CTSC2016] 萨菲克斯·阿瑞
skipped
CF1456E XOR-ranges(数位 dp、独立性)
无限制的位可以轻松删掉,我们要不对每个数处理出 \(O(m)\) 种情况,每种情况都形如固定了这个数的一个后缀(一堆高位),然后没固定的地位可以随便填。好的,这样就发现变成了和一个最值有关的类似笛卡尔树的结构,记录两边的状态,从中间枚举一个比它们高的状态断开,计算贡献,复杂度大概 \(O(n^6)\sim O(n^7)\)(视 \(n, m\) 同阶),很难受。
发现每一位是独立的,很诱人,我们删掉状态里的两个 \(n\) 和转移里的一个 \(n\) 将其改为一个 \(n\) 表示当前只考虑低 \(b\) 位。这样的话就好一点,两边的状态在记下 \(l, r\) 后直接变成 \(O(1)\),然后可以转移,要么宣布这一位考虑完了,要么从中间找一个断点分割。复杂度 \(O(n^4)\)。
(skipped)CF1081G Mergesort Strikes Back
skipped
CF1707D Partial Virtual Tree
可以给每个节点分配一个时间戳 \(t_i\),表示这个点在 \([0, t_i]\) 这些集合里,不在 \((t_i, k]\) 这些集合里,那就是要做一个分配标号的事情。那如果是这样,就会发现这个标号要求在值域上是连续的一段,不能有空隙,但是空隙是在全局考虑的,这样会很麻烦。所以不妨容斥 \(t\) 个 \(\subsetneq\) 变为 \(=\),其他变为 \(\subseteq\)。
然后就 dp。具体看一下限制是 \(\min(t_u, t_v)\leq t_{lca(u, v)}\),然后发现这个 \(t_u,t_v\) 越大限制越严格,于是 \(dp_{u, j}\) 表示 \(u\) 子树内最大的 \(t\) 为 \(j\) 的方案数,你再仔细想一下就发现限制是说子树上来的最大值的次大值 \(\leq t_{lca(u, v)}\),然后分成两种情况讨论一下,也不是很难,就不说了。
(未完成)CF1647F Madoka and Laziness
CF1349F1 Slime and Sequences (Easy Version)(构造映射、欧拉数)
你可以用任何方法推断出好序列的数量是 \(n!\),甚至可以求出 \(p=1\) 的答案,但是为了能用合理的时间复杂度过题,你需要得到题解的 指引:
首先我们可以通过暴力算出长度为 \(n\) 的好序列数量为 \(n!\),这就指引我们将好序列与排列作映射。
你从小到大枚举数字,将这种数字的出现位置从后往前地放入一个排列的末尾,直到最后有一个长度为 \(n\) 的排列,这是将好序列映射为排列;有一个排列,则它的极长下降前缀就是一个好序列中 \(1\) 的出现位置,下一个极长下降区间是 \(2\) 的出现位置,以此类推,你发现这个刚好和题目限制能对上,那就把排列映射成了好序列,至此形成双射。想要统计答案,只需要统计排列的第 \(i\) 个位置作为好序列中数字 \(j\) 的出现会有多少贡献,不难发现只是限定了 \([1, i]\) 的极长下降段个数,使用一个欧拉数(Eulerian Number - OI Wiki)和一个组合数计算即可。
\(A(n, m)\) 表示有 \(m\) 个位置满足 \(a_i<a_{i+1}\) 的排列的个数。有 \(A(0, m)=0, A(n, 0)=[n>0]\) 以及
只需要讨论数字 \(n\) 插在哪里即可。可以通过 F1。
P5359 [SDOI2019] 染色
auto sqr = [&](mint x) { return x * x; };
mint coe[7][7] = {
{0, 0, 1, 1, 1, 0, 1},
{0, 0, c - 2, c - 3, c - 2, c - 2, c - 3},
{1, 1, 0, 0, 0, 1, 1},
{c - 2, c - 3, 0, 0, c - 2, c - 2, c - 3},
{c - 2, c - 2, 0, c - 2, 0, c - 3, c - 3},
{0, c - 2, c - 2, c - 2, c - 3, 0, c - 3},
{mint(c - 2) * (c - 3), sqr(c - 3), mint(c - 2) * (c - 3), sqr(c - 3),
sqr(c - 3), sqr(c - 3), c - 3 + sqr(c - 4)},
};
g[0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < 7; j++) {
for (int k = 0; k < 7; k++) g[i][j] += g[i - 1][k] * coe[k][j];
}
}
这题太难写了,你首先去按列做 dp,记下这一列的两个颜色,然后去转移,理论可以进行转移,但问题主要是状态数太多。想法是加速相邻两个非空列之间的转移,我们来看看有多少种本质不同的非空列,发现:
有以下 \(7\) 种情况需要考虑,为了枚举方便我们就不压缩等价状态了。
a..a a..a a..b a..b a..c a..c a..c
b..b b..c b..a b..c b..a b..b b..d
要求 \(a, b, c, d\) 为互不相同的数。但是需要注意左右两侧的 \(a, b, c, d\) 表记的是两个非零列的状态,也就是说左右两侧是已经确定的,轮不到我们去计算 \(c, d\) 的取值方案数。
我们预先处理这些系数,记 \(g_{i, j}\) 表示中间有 \(i-1\) 个非空列(还是 \(i\) 个非空列记不清了),状态的名字是 \(j\)。
那么 \(g\) 的转移怎么写,以 ac(右侧列)为例,有以下 \(7\) 种情况:
ab -> ac:\(0\),因为有相等,非法了。ac -> ac:\(0\);ba -> ac:\(1\),ba是固定的,一定合法了;bc -> ac:注意现在我们决策的是bc这一列当中的 \(c\),要求这个 \(c\neq a,b\),同时还要知道那个bc列的 \(c\),其左侧的不等限制已经被左侧满足,不需要再次考虑,\(c\) 只要 \(\neq a, b\) 就能与它左边不等。所以这里的系数是 \(m-3\)(\(m\) 是颜色数),分别与 \(a, b\) 和ac中的 \(c\) 不等。- ……之后的情况都没什么好说的了。
这些系数属于理论可写,实际难写的,写完了之后就可以做 dp 了,反正就是枚举状态然后转移,然后可以写出一个 7K 的 dp。
然后套用 P5358 [SDOI2019] 快速查询 的数据结构即可完成本题。
(未完成)P5320 [BJOI2019] 勘破神机
P9108 [PA2020] Malowanie płotu
这个题首先要读懂题意,然后比较简单,直接优化 dp 即可。因为与 \([l, r]\) 有交的区间是全部区间减去无交的,无交的区间要么右端点 \(<l\) 要么左端点 \(>r\),可以开两个 \(O(m)\) 的数组 \(pre, suf\) 记下。进一步的不需要将所有区间的 dp 值全部算出,反正最后也要喂给 \(pre, suf\),直接固定左端点或右端点算就完了。
P10681 [COTS 2024] 奇偶矩阵 Tablica
这题,首先要往一个合理的方向想。根据题解(COTS/CETS 2024 奇偶矩阵 Solution - 洛谷专栏),我们枚举 \(n\) 行中有 \(a\) 个 \(1\),\(b\) 个 \(2\),\(m\) 列中有 \(c\) 个 \(1\),\(d\) 个 \(2\)。然后发现固定 \(a\) 之后两外三个都可以算。选择对应的行和列 \(\binom n a\binom m c\),接下来就是要做一个匹配问题,可以看成是:把应该在同一行的数字先拆成两行,将列上的数字按照行的顺序拍成一列,排一下 \((c+2d)!/2^d\),然后再让行过来选列,行的顺序已经被前面那个组合数固定了,因为同一行内两个数字的话会算重,再除一个 \(2^b\)。剩下一个问题就是会不小心使得一个格子内出现一个 \(2\),枚举有多少个 \(2\),然后做容斥。
这里 \(/2^{b+d-t}\) 而不是 \(/2^{b+d-2t}\) 是因为出现 \(2\) 的时候“同一行内两个数字的话会算重”会除个寂寞,把它调回去。
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/18659474
浙公网安备 33010602011771号