[NOIP2022] 建造军营
前言
米奇妙妙 \(\rm{dp}\) , 也是高端计数
这种题看得懂想不出, 还是非常难蚌
能不能多想想再去看 \(\rm{TJ}\) 啊
算法
思路 \(1\)
注意到除了割边, 其他的边都没有影响, 显然可以缩 \(\rm{e}\)-\(\rm{DCC}\) 再进行处理
这里发现缩完之后形成一棵树, 考虑树形 \(\rm{dp}\)
这里我有一个误区, 就是答案必须要是几个 \(dp_x\) 组合到一起, 但是实际上完全不用
对于树这种特殊结构, \(dp_{root}\) 已经包含了所有情况
因此令 \(dp_{x, 0/1}\) 表示只在 \(x\) 的子树之内建造军营, 军营的
首先考虑答案式子, 我们注意到, 答案应该是多种情况累加, 因为 \(dp_{x, 1}\) 实际上是不重不漏的, 主要需要处理的是那些无关紧要的边, 我们选择守或者不守
注意到如果缩完点之后当前子树的大小为 \(Size_x\) , 我们就有 \(Size_x - 1\) 条割边要守, 其中子树外的 \(m - Size_x + 1\) 条边可以任选
这里有一个超级巨大的问题: 对于当前节点到父节点的边, 一定不能选, 因为对于父节点, \(dp_{dpa, 1}\) 实际上考虑了这条边, 选上之后会导致重复, 因此记录答案时不能算这条边
事实上如果最早就有关于答案式的思路, 可以通过手推样例/观察性质发现这种性质, 但是我还是大为震撼
所以答案为
太有实力啦
考虑递推
首先我们需要知道, 如果对于 \(x\) 的子树 \(son\) , 其中 \(son\) 内部有军营, 那么 \(x \to son\) 这条边必须要守, 其他无所谓
所以有
初始化
注意这里表示的是只选 \(x\) 这一 \(\rm{e}\)-\(\rm{DCC}\) 中点的可能性, 反正可以随便选, 边随便选的情况在计算答案时统计
思路 \(2\)
考虑复习
感觉之前的做法过于巧妙, 遂决定重新理解一种更一般的做法
简化问题
首先理解题意
求选出点集 \(\mathbb{S}\) 和边集的方案数, 使得任意切断边集中的一条边, 点集中的点仍然联通
你发现如果切掉的不是割边, 一定没有用, 所以考虑先缩成一棵树, 这样留下来的边才有讨论价值
注意这样做会多出来一个 \(2^{|\mathbb{E}| - \epsilon}\) 的系数, 其中 \(\epsilon\) 表示割边的条数, 其意义是非割边随意保护或者不保护
还需要注意的是, 现在的一个点可能在原图中表示多个点, 这个再计算时是需要处理的
以下用 \(\varepsilon\) 表示该点在原图中代表多少点, \(\mu\) 表示该点在原图中包含了多少条边
好的, 经过上面的转换, 我们只需要解决这个问题的树上版本, 不难想到树形 \(\rm{dp}\)
分类讨论 + 转移
记 \(dp_{u, 0/1, 0/1}\) 表示对于 \(u\) 子树, 子树内是否有 \(\mathbb{S}\) 中的点, 子树外是否有 \(\mathbb{S}\) 中的点
第二个 \(0/1\) 方便合并时处理 \(u \to v\) 的边是否被保护
\(dp_{u, 0, 0}\) 和 \(dp_{u, 0, 1}\) 的转移
不难发现
表示 \(u \to v\) 可选可不选的方案数
最后处理 \(u\) 不选的方案数
\(dp_{u, 1, 0}\) 的转移
你发现其必然可以从 \(dp_{u, 0, 0}\) 和 \(dp_{u, 1, 0}\) 合并而来, 具体的
合并最后处理
分别表示 \(u\) 被选和可选可不选5
你发现这样子, 第三维的 \(0/1\) 会爆炸, 所以特殊转移
令 \(g_{u, 0/1}\) 表示确定合并的时候要选 \(v \to w\), \(f_{u, 0/1}\) 表示确定合并的时候不选 \(v \to w\) , 第二维意义不变
不难有
最终
\(dp_{u, 1, 1}\) 的转移
你发现这种情况下, 无所谓 \(u\) 点选不选择, 对于 \(v\) 子树中包含 \(\mathbb{S}\) 中的点的情况, 都必须要选上 \(u \to v\)
合并最后处理
太乱了, 写了很久也不能用, 显然需要更具体一点, 上面的也不一定正确
合并子树 \((\)不考虑 \(u\) 节点\()\)
\(dp_{u, 0, 0}\) 和 \(dp_{u, 0, 1}\)
不难发现
\(dp_{u, 1, 1}\)
不难发现
\(dp_{u, 1, 0}\)
这个唯一比较复杂
首先是分成很多种情况
- 没选 \(u\)
-
只选一棵子树 \((\)此时 \(u\) 肯定不选\()\)
考虑记这种情况的方案数为 \(f_{0/1}\) 表示是否选了子树
转移类似 \(dp_{u, 1, 1}\)\[\begin{align*} f_0 \gets dp_{u, 0, 0} \\ f_1 \gets f_1 + f_0 \times 2dp_{v, 1, 0} \end{align*}\]需要注意的地方是, 初始化 \(f_0\) 时使用的 \(dp_{u, 0, 0}\) 应当是还没有被更新的
然而你发现这样子「確有問題」, 具体在于除了最后一颗子树, 其他子树的系数 \(f_0\) 都不能表示「其他子树不选」, 所以我们需要对其进行修改
观察 \(dp_{u, 0, 0}\) 的转移式, 我们考虑计算出 \(\displaystyle C = \prod \limits_{v \in \rm{son} (u)} 2dp_{v, 0, 0}\)
转移变为\[\begin{align*} f_1 \gets f_1 + \frac{C}{2dp_{v, 0, 0}} \times 2dp_{v, 1, 0} \end{align*}\]进一步的, 直接令 \(f\) 表示 \(f_1\)
\[\begin{align*} f \gets f + \frac{C}{dp_{v, 0, 0}} \times dp_{v, 1, 0} \end{align*}\] -
所有选了点的子树都和 \(u\) 有连边 \((\)此时 \(u\) 不选\()\) , 至少选两棵子树
这种情况只能正难则反, 考虑减去不合法的情况, 即减去一个都没选的方案数, 再减去只选了一个的方案数, 同上转移, 仅仅把系数 \(2\) 去掉即可, 还有把第三维改成 \(1\)
-
- 选 \(u\)
- 所有选了点的子树都和 \(u\) 有连边 \((\)此时 \(u\) 选\()\)
每个儿子可以任意选或者不选
- 所有选了点的子树都和 \(u\) 有连边 \((\)此时 \(u\) 选\()\)
实现
框架
先计算各个边双联通分量的信息, 建好树之后如上开跑即可
代码
在此之前先用数据验证算法正确性
总结
神秘计数方法
\(\rm{dp}\) 式子考虑不到的, 可以在统计答案时考虑
注意计数不重不漏, 注意初始化
连通性问题常常要往缩点计数类的方向去想
缩点计数类问题一定要注意缩点带来的系数
一种合并类问题, 具体的套路在 之后的一篇文章 中有提到, 不在这里赘述
善于运用辅助数组避免新开一维或者更复杂的讨论
分类讨论能力必须加强, 考虑清楚每一维的含义

浙公网安备 33010602011771号