CF1434E A Convex Game
tag:DP,交换值域,博弈论,凸性。
首先,由于每组数是分开考虑的,题目可以看作一个组合游戏,也就是说我们可以分开考虑每个游戏的 SG 值。对于每一组游戏,套路地考虑构建一个有向图模型。注意到每一步的选择与差是有关的,考虑记录 \(f_{i, j}\) 为,现在 \(b\) 数组的最后两位为 \(a_{i}, a_{j}\),SG 值是多少。转移是好写的,\(f_{i, j} = \text{mex} \{f_{j, k} \mid a_{k} - a_{j} > a_{j} - a_{i}\),需要倒过来考虑。显然,时间复杂度为 \(\mathcal{O}(n^{2})\)。(记游戏组数为 \(T\)。)
考虑发掘性质。下凸是个容忽视的条件,这提示我们差分数组在最坏情况下为 \(1, 2, 3, \dots\)。那么考虑转移最长链,有 \(len = \mathcal{O}(\sqrt{V})\),也就是 \(f\) 数组现在所记的值分布在 \([0, \sqrt{V}]\) 中。于是转换值域,记 \(g_{i, j}\) 为 \(b\) 数组倒数第二位为 \(a_{i}\),SG 值取 \(j\) 时的合法末尾集合,形式化的,\(g_{i, j} = \{x \mid f_{i, x} = j\}\)。但是这个没有解决信息过多的问题。注意到在固定倒数第二位和 SG 值之后,下一位选得越远,上一位可转移的位置更多,而且是包含关系,因此不妨记 \(g_{i, j} = \max \{x \mid f_{i, x} = j\}\),这样信息量就降下来了。
在此基础上,重写一下 \(f_{i, j}\) 的转移:\(f_{i, j} = \text{mex} \{x \mid a_{g_{j, x}} - a_{j} > a_{j} - a_{i}\}\)。这个 \(\text{mex}\) 还是十分不好看,进一步转化为 \(f_{i, j} = \min \{x \mid a_{i} \le 2 a_{j} - a_{g_{j, x}}\}\)。发挥人类智慧,反向扩句一下,有 \(f_{i, j} = x\) 满足:
不难发现固定 \(j, x\) 后,合法的 \(i\) 在第一个式子里是一段后缀,在第二个式子里是一段前缀。取最严的限制,记 \(h_{i, j} = \min_{k = 0}^{j} \{g_{i, k}\}\),那么合法的 \(i\) 满足 \(a_{i} \in (2 a_{j} - a_{h_{j, x - 1}}, 2 a_{j} - a_{g_{j, x}}]\)。
下面考虑在转移中去掉 \(f\)。有了上面的框,显然可以按 \(j\) 从小往大考虑 \(g\),消除掉 \(\min\) 的影响;相应的,从后往前考虑 \(g\),同样可以消掉 \(max\),那么现在的问题转成一个覆盖问题了。
记得手算上界。
P6778 [Ynoi2009] rpdq
tag:根号算法,根号分治,分块。
有莫队二次离线做法,有时间写写学习笔记,先摆一下这个科技的应用范围:
- 是个莫队题;
- 加删点的贡献形式有关当前区间(长度);
- 同时这个影响可用前缀写成差分的形式。
大概可以把 \(\mathcal{O}(n k \sqrt{n})\) 降到 \(\mathcal{O}(n \sqrt{n} + n k)\),\(k\) 为更新贡献的复杂度。此题还有 top cluster 树分块科技做法,同样不做深究。
先拆贡献,\(\sum_{i = l}^{r} \sum_{j = l}^{r} dist(i, j) = \sum_{i = l} ^{r} \sum_{j = l}^{r} dis_{i} + dis_{j} - 2 dis_{lca}\),两个 \(dis\) 的部分化成 \(2 (r - l + 1) \sum_{i = l}^{r} dis_{i}\) 不用管了,重点在于 \(\sum_{i = l}^{r} \sum_{j = l}^{r} dis_{lca}\)。
从特殊性质开始考虑。对于 A,容易做到对每个点计算其作为 \(lca\) 的次数,扫儿子前缀和或者某种换根 DP 都是可行的;对于 C,\(\mathcal{O}(n q)\) 只需要在上述做法的基础上转换 \(sz\) 的定义。但这很没必要,毕竟非关键点的答案在询问中是不重要的,先对关键点建立虚树再做就可以可以达到 \(\mathcal{O}(\sum r - l + 1)\),注意消 \(\log\) 依赖于建虚树的 trick。
理一下 C 性质的两种做法,于是现在手头上有了:
- 钦定点集 \(S\),\(\mathcal{O}(n)\) 求对于每个 \(i\) 的 \(\sum_{j \in S} dis_{lca}\);
- 钦定点集 \(S\),\(\mathcal{O}(\lvert S \rvert)\) 求 \(\sum_{i, j \in {S}} dis_{lca}\)。
于是可以尝试上分块了。对编号分块,设块长为 \(B\),可以用做法 1 \(\mathcal{O}(\dfrac{n}{B} n)\) 预处理点集每个块时各个点做 \(lca\) 的贡献,前缀和之后不难处理整块之间和整散之间 \(\mathcal{O}(\dfrac{n}{B})\) 的询问。(二维前缀和可以 \(\mathcal{O}(1)\)?)散块之间则考虑用做法 2,做完了。
剩下的在于常数优化。
QOJ1197 Draw in Straight Lines【数据不全】
tag:图论,网络流,最小割模型。
非常困难的题,要慢慢来。对操作进行人类思考,发现最后的涂色顺序应该是黑色长条、白色长条、单点修改。于是考虑确定什么信息之后计算花费,显然,需要也只需要 \(b/w_{r/c, i, j}\) 这四种值。
下面进行分析。对于点 \((i, j)\),如果 \(b_{r/c, i, j} = w_{r/c, i, j} = 1\),显然不优:
- 若这两次黑白染色的区间不互相包含,那么由于白色在后面染,可以让黑色少染一截;
- 若白色包含黑色,直接不染黑就好了;
- 若黑色包含白色,由于一个格子最多染两次,不难发现被后面染白的格子只可能被这两次操作影响,那么可以直接把染黑的操作断成两截做,中间不染白。都是两次操作,不用在意 \(b\) 的影响。
于是对 \((i, j)\) 的目标颜色进行分讨:
- 黑色:\(w_{r/c, i, j} = 0\),且若 \(b_{r/c, i, j} = 0\) 的话需要用 \(c\) 的代价单独染色;
- 白色:\(b_{r, i, j} + b_{c, i, j} < 2\),且若 \(b_{r/c, i, j} = 1 \land w_{c/r, i, j} = 0\) 的话也需要单独染色。
但上面这段分析只包括了 \(c\) 的贡献系数。对于 \(a\),显然有 \(a \sum b/w_{r/c, i, j}\) 的贡献;而对于 \(b\),我们钦定一个方向计算开头次数,即 \(b \sum (b/w_{r, i, j} \overline{b/w_{r, i, j - 1}} + b/w_{c, i, j} \overline{b/w_{c, i - 1, j}})\),其中 \(\overline{x}\) 表示 \(x\) 取反,毕竟是 boolean。
然后怎么办呢?发现选或不选是相对的,因此考虑最小割建模,割一条边表示选择对应情况,吃对应贡献。
- 基础模型:\(a\) 的限制是好搞的,可以建 \((S, b_{r}/w_{c}, a), (b_{c}/w_{r}, T, a)\),交错连边的分法有点讲究;
- \(b/w\) 不同时选也比较 trivial,可以建 \((b_{r/c}, w_{r/c}, \infty)\) 的边(\(c\) 是建反边)。这个对应 \(b/w\) 的交错连边;
- 其他限制:对于黑点,容易的,\(w_{r/c}\) 都不选,连 \((w_{r/c}, T, \infty)\);且 \(b_{r/c}\) 都不选的话要 \(c\) 的代价,连 \((b_{r}, b_{c}, c)\)。这个对应 \(r/c\) 的交错连边;/
- 对于白点,神秘的,\(b_{r} \land b_{c} = 0\) 的限制,要建 \((b_{c}, b_{r}, \infty)\) 的边;\(b_{r/c, i, j} = 1 \land w_{c/r, i, j} = 0\) 的限制,要建 \((w_{c/r}, b_{r/c}, b)\) 的边。
CJ2036 【2025省选模拟十九】在乡村 V
tag:DP,优化状态数。
错误思路
记 \(f_{i, j}\) 为做完 \([1, i]\) 的工作,用了 \(j\) 个金币的最小时间。是个完全不能优化的奇怪 \(\mathcal{O}(n m)\)。
正确思路
第一步应该是个经典的 DP 优化,交换值域,(或者交换定义?)设 \(f_{i, j}\) 为做完 \([1, i]\) 的工作,在 \(j\) 的时候停止的最小金币花费。
看上去更不能做,所以发掘一下性质看能不能优化状态设计。发现 有效的结束时间 就两个:\(k d, k d + x\),也就是卡着极限去转移。因为 复合人类思维的贪心,让帕鲁休息的时间越短越好,也就是让工作时间尽量连续。于是对于 \(f_{i, j}\) 的转移,找到后面第一个 \(t\) 使得连续完成 \([i + 1, t]\) 的工作之后正好在睡觉来转移。转移式就 trivial 了,尝试不使用金币等到第二天,或者使用金币当天截止一边在睡觉之前再给个任务(我勒个带资本家呀)。
但是现在还 无法确定 有效的 \(j\) 的数量,做不到优化。简单的思考一下,如果每次转移只回退一个晚上,那对于 \(i\) 来讲可能的结束时间就真的只有 \(\mathcal{O}(n)\) 个了。正确性有点玄。倘若最后剩下若干金币,怎么分最优呢?感性理解一下就是,前面的分段是最优的,那么把金币分到之前去使得连续短整体平移,代价是比较大的,不如直接分到末尾,从后往前删。
于是考虑枚举一个后缀,把它们强制变成一个连续段扯出来,把剩下的金币全往上面堆,以此统计答案。这就完了。为什么是连续段呢?因为连续段的限制是最紧的,如果有空掉的睡觉时间,显然在减少时间相同的情况下扔金币到晚上代价最小,大概就得证了。
problem:为什么时间可以优化状态数而金币不可以?怎么想到分金币的方法,真的显然吗?
实现细节
- 注意到 \(t\) 的位置 只与 \(i\) 的位置和 \(j\) 为 \(k d\) 还是 \(k d + x\) 有关,可以预处理;
- 有效的 \(j\) 是 \(\mathcal{O}(n)\) 个的,但是它的 值域 不是。初步想法是用 \(2 j - 1, 2 j\) 表示 \(j d, j d + x\),同样预处理一下找到 \([1, i]\) 全部连续的下界和 \([1, i]\) 全部不优化的上界。诶,还是会爆,这就只能记录上述上下界为 \([l_{i}, r_{i}]\),转移的时候直接判断是区间内的第几个值了;
- \(\mathcal{O}(n) \neq n\),实际上应该开到 \(2 n\) 吧。
写起来细节十分痛苦。
POJ2022D Sum of LCS
tag:计数,容斥,枚举。
POJ 有点厉害。考场不知道哪里来的耐心对着假做法调 4 个小时(
错误思路
考虑转换枚举对象,从枚举两个数变成枚举 LCS 来计数。钦定 LCS 在两个数中的两端数字不一样,保证长度,这个容斥一下可以提前数位 DP 计算。注意到 \(len \le 2\) 的 LCS 会算重,因为数字长度可以达到 \(6\),于是对 \(len = 2\) 的直接枚举去重;\(len = 1\) 的考虑在算上面所有东西的时候都减 \(1\),然后 \(res \gets res + n^{2}\) 并去掉所有数字集不交的数对,这个可以用枚举子集做。时间复杂度忘了但是对的。
看上去是个很厉害的做法,怎么假的呢?\(111\) 会把 \(11\) 算两次,大概就是这样。
正确思路
随手记一个 trick:\(x = \sum_{i \ge 1} [i \le x]\)。
第一步转化还是对了,但没完全对。枚举一个子串,计算两者都包含这个子串的对,这个是极其好做的,开个 std::map
就没了。容易算重,怎么处理呢?注意到这个子串和 LCS 的关系是 类似集合包含的,那么不妨从容斥角度想想。
观察 需要去重的情况:一、子串与 LCS 包含;二、多个 LCS。主要是第二个比较不好用普通的容斥算,怎么办呢?由于 \(n \le 10^{5}\),对于 同长度子串(第一项,直接容斥),可能的子串集大小最多为 \(2^{6} = 64\),于是往 std::map
里面扔个可重集就完了!
实现细节
- 子串要进行
std::unique
,不然要用二项式之类的东西; - \(\sum_{i = 1}^{n} (-1)^{n - i} \dbinom{n}{i} = [n \ge 1]\),于是拆一下式子发现不需要预处理系数。
AGC055F Creative Splitting
tag:计数,建立双射。
懈怠了,有些题没有总结呀。困难的金牌题,稍微记一下部分分。不是,\(\pmod {998244353}\) 那块是什么鬼,给科技的吗?
部分分思路
-
10 pts:\(n, k \le 6\),观察到判定一个 \(a_{i}\) 是否合法只需要关心它在当前数列的第几位,于是考虑记录所有数列的长度。状态数 \(\mathcal{O}(k^{n})\),转移 \(\mathcal{O}(k)\),总复杂度玄学;
-
30 pts:考虑对上述状态进行优化。还是由于判定 \(a_{i}\) 的合法性不用关心上一位,对于目前长度相同的数列直接合并好了。也就是,状态记录有多少个长度为多少的数组,总状态数是 \(\sum_{i = 0}^{n} \dbinom{n}{n - i} \times (n - i)^{i}\),写个暴力算出来大概是 \(2e6\) 级别。
写的时候可能有点细节。比如对前后缀做一下以满足扣掉每一位的计数,以及状态可能要哈希一下。
正确思路
第一步还是对的。按照原题模拟,充要条件不好找,于是可以想到双射。首先考虑怎么判定一个数列是否合法,往数列里加数 限制是逐渐变严的,故考虑倒过来 贪心构造可行解。可以这样简单的描述构造过程:
设一个长为 \(n\) 的数组 \(c\),表示各个数组的限制,初始 \(c_{i} = k\)。从后往前枚举 \(b_{i}\),找到最小的 \(c_{j} \ge b_{i}\),\(c_{j} \gets c_{j} - 1\)。如果找不到,寄。
然而光有这个 大小关系 是不好计数的。考虑转成对满足某个大小条件的值的 数量 进行操作;具体的,设 \(d_{i} = \sum [c_{j} < i]\),于是构造过程可以直接表示如下:
初始 \(d_{i} = 0\),从后往前枚举 \(b_{i}\),找到第一个大于等于 \(b_{i}\) 的位置 \(p\),使得 \(d_{p} \neq d_{p + 1}\),\(d_{p} \gets d_{p} + 1\)。如果找不到,寄。
看上去没有简化,但是注意到 瓶颈在于 找到 \(p\),从这方面下手。\(d\) 是单调不降的,原本 \(d_{b_{i}}\) 到 \(d_{p}\) 是相同的,于是操作可以简化到 只与 \(b_{i}\) 有关:
初始 \(d_{i} = 0\),从后往前枚举 \(b_{i}\),若 \(d_{b_{i}} = n\),寄;否则,\(d_{b_{i}} \gets d_{b_{i}} + 1\),然后对 \(d\) 重新排序。
这就爽了,直接上套路考虑枚举最终序列,然后计数。留作习题吧,还是有点困难的。
实现细节
- DP 的数组下标从 0 开始,也就是说初始化在 -1。以后能写滚动数组就写滚动数组得了。
CF1842F Tenzing and Tree
tag:树的重心,构造。
PKUWC 回来搞一下专题复健,整理掉当年速通的题。
正确思路
Step 1:考虑 确定了黑点 之后,怎么确定答案。确定一个根,则每条边的贡献为 \(\lvert key_{x} - (k - key_{x}) \rvert = \lvert 2 key_{x} - k \rvert\)。考虑拆掉绝对值,发现这个 两倍相减 的形式很像某个东西,于是人类智慧一下,直接选择根为这几个黑点的 带权重心,式子就可以拆成 \(2 key_{x} - k\) 了。
Step 2:考虑 确定了根 之后,怎么放点会最优。放一个点时,\(ans = n - 1\);放第二个点时,与第一个点中间连的边 \(con \gets con - 1\),而其他点 \(con \gets con + 1\);放第三个点时同理……容易发现,围着根一层层 BFS 式的扩展,是最优的。
Step 3:考虑结合上面那俩玩意儿。如果直觉够强,会注意到 Step 2 的方法是 \(\mathcal{O}(n)\) 的,只不过问题在于 没有根。所以 \(\mathcal{O}(n)\) 钦定一个根计算答案,全部取 \(\max\) 就可以 \(\mathcal{O}(n^{2})\) 的总复杂度解决了。取 \(\max\) 的正确性?注意到如果计算答案时不使用带权重心,可能的结果只有出现负数贡献,这显然不优,并不会影响到取 \(\max\) 后的答案。
由此也可以观察到,树的重心在 有关子树大小的树上最优化问题 中发挥了比较大的作用。
1767F Two Subtrees
tag:根号算法,莫队,众数,dfn 序,dsu on tree。
有点人类智慧,看一下能不能独立再想出来。
正确思路
众数 已经把 莫队 两个字写到脸上了,下面考虑怎么从这上面下手。首先需要确定派平方式,常用的两种序列 欧拉(环游)序和 dfn 序 中,欧拉序适用于 路径统计,而此题又是子树问题,dfn 序明显更有优势,所以在 dfn 上考虑 进行一个队莫的维四。 不行,这个太垃圾了,我们需要一点带有 dfn 序特性的东西。
一个直观的优化是,怎么把两个子树的四个端点,变成 两个端点。考虑子树在 dfn 序上的表现是一段区间,是否可以考虑 将一段区间看作端点 进行莫队呢?这是可以的,不过需要一点手段来 保证复杂度。具体的,分析一下莫队的复杂度来源其实在于 端点的移动,所以我们需要一个优秀的子树区间移动方法。
也就是 dsu on tree 的分配方式。按 dsu on tree 的时间复杂度分析,每一个块内右端点移动总量 \(\mathcal{O}(n \log n)\),左端点则按照块长定,总复杂度大概是打爆四维莫队了。加之 dsu 的小常数,可以快得飞起。
忘记说众数的实现了。朴素的实现可以记录 \(cnt_{x}\) 表示 \(x\) 的出现次数、\(app_{i}\) 表示 \(cnt_{x} = i\) 的个数,由于加减数只会对 \(cnt_{x}, app_{i}\) 有 \(\mathcal{O}(1)\) 的影响,所以可以 \(\mathcal{O}(1)\) 直接维护 \(mx\) 指针。但现在需要对众数取 \(\min\),上述做法只能快速维护众数出现次数,求具体众数还需要 \(\mathcal{O}(n)\) 扫一遍,可以尝试使用小常数 值域分块,或者大常数 std::set
。值域分块的话是 \(\mathcal{O}(1) - \mathcal{O}(\sqrt{V})\) 的,也没啥可以优化,本质是不超过 \(\mathcal{O}(\dfrac{n}{B})\) 次的大跳和不超过 \(\mathcal{O}(B)\) 次的小跳,写一下对比吧。(upd:std::set
被打爆了……)
实现细节
- 如果正确分块,重儿子置前置后应该是没有区别的;
- 如果用
std::vector
存图,重儿子可以直接std::swap
到 \(G_{x, 0}\),然而我不用; - 发现
std::max<int>(x, y)
可以解决double
和int
不兼容的问题; - 注意块长的计算方法。莫队的时间复杂度是 平衡左右端点 得来的,不失一般性的,设块长 \(B\),有右端点 \(\mathcal{O}(\dfrac{n \log n}{B} n \log n)\),左端点 \(\mathcal{O}(q B)\) 的复杂度,可以平衡出 \(B = \dfrac{n \log n}{\sqrt{q}}\),比较优秀。
P9021 [USACO23JAN] Subtree Activation P
tag:树形 DP,虚点,回路,dsu on tree。
更加人类智慧。
错误思路
由于题目给的条件是 子树覆盖,直觉告诉我们这个是 dsu on tree。然而并不全对,只能说答案上界是 \(\mathcal{O}(n \log n)\),因为当重儿子可选的时候转移数还是不一定相同的。于是尝试从类似 dsu 转移的角度入手优化。
考虑刻画一下问题。只关心两点之间移动的贡献,若子树包含则为 \(\lvert sz_{x} - sz_{y} \rvert\),否则为 \(sz_{x} + sz_{y}\)。那么就是要找到一个经过所有点的回路,使其边权最小。但此时有 两种边权,加之连边 方式自由,还是不好导出做法,需要限定路径形态。
发现第二种边权由于 不同时依赖 \(x, y\),可以看作一个单独的回路开头考虑;剩下的部分则由第一种边来连更优。于是就可以对着欧拉序套路 DP 啦!具体的,回路 的特殊之处在于每个点不管连谁,只要保证度数为 偶数 就一定是对的。所以记 \(f_{x, 0/1}\) 表示点 \(x\) 当前连边 \(\pmod 2\) 时的情况,转移就 trivial 了……吗?
实际上第一种边还是有问题的。发现 两者包含并不代表父子关系,也有可能是祖孙关系,这就难绷了。那么考虑再记一维 \(0/1\) 表示这层关系,转移时由于绝对值可以自动解除,提前计算贡献就完了。
正确思路
还是太菜,倒在了 DP 分析设计的那一步。
做法一:实际上,确实是若干 欧拉环游路径 更优,只不过在刻画的时候漏掉了一步:直接划分是 不能保证闭合 的,需要一点手段强制闭合。由于点的起始位置不定,直接记录是不现实的。于是考虑 建一个虚点,与其连边 \(sz_{x}\),那么问题可以强化为 \(1 \sim n\) 与这个虚点 互相连通且形成一个大的回路,于是剩下的一维用来补上虚点状态就好了。
做法二:先不设计 DP,继续观察由第一种边连成的图有什么性质,再次 强化限制。经过 手模 发现,最优路径一定是一段上行加一段下行,否则断成两条回路是会更优的;在这种情况下,一条回路的贡献反而方便计算,就是两倍最浅点的 \(sz\)。此时一个只有两种状态的 DP 就 done 了,剩下的留给年枫好好想想。
P4886 快递员
tag:点分治,重心,树上二分。
树上二分是自己取的名字,感觉本质确实是这个。第一边做这个思维题感觉很新鲜的一个点分治,再看已是套路,但还是很好玩。
不过有个 \(\mathcal{O}(n \log^{2} n)\) 或更优秀的 \(\mathcal{O}(n \log n)\) 直接维护每个点答案的做法,难点在于换根的计算,不做细讲。然而正解也没多详细。
正确思路
\(\mathcal{O}(n^{2})\) 的暴力大众分是简单的,对每个点 \(\mathcal{O}(n)\) check 一下就没了。
考虑发掘性质,根据草履虫实验得知,每次钦定一个快递站当根算完答案后,把快递站 往当前答案最长链放是可能更优的。然而单次查询还是 \(\mathcal{O}(n)\) 不能优化,还要发掘性质。
不难得知有任意条分居俩子树,或最长链跨过当前点时,不管往哪边跳都不优,所以 每次移动方向固定,可以用 重心分子树小于等于 \(\dfrac{n}{2}\) 的理论加速这个移动过程。于是完了。
实现细节
- 判断点在路径上显然可以暴力 \(lca\),但有更聪明的方法:对每个子树标记颜色就好了。
P9669 [ICPC2022 Jinan R] DFS Order 2
tag:树形 DP,回滚背包。
艹,好像又有点快了。压一压速度,提一提质量。不行,还是太简单了(
正确思路
还是能比较自然地想到 DP 的。硬要说突破口的话,可以考虑 DFS 与树形 DP 的强关联性,或者概括为 DFS 先处理子树再处理兄弟 的顺序特点。故可以设 \(f_{x, i}\) 表示 \(x\) 在第 \(i\) 位的方案数。
对于 \(x\),需要在 \(f_{fa, i}\) 的基础上加入兄弟得到 \(f_{x, j}\)。于是,我们需要得到兄弟填完子树的方案数,还要算上 \(k! (cnt_{fa} - k - 1)!\) 的贡献(\(k\) 为前置儿子数),即前后乱排的状况。填子树的方案 \(coe_{x}\) 还是好算的,\(coe_{x} = \prod_{y \in son_{x}} coe_{y} \times cnt_{x}!\),直接 DP 就完了。
考虑从 \(x\) 转移到 \(y \in son_{x}\) 的时候,放 \(i\) 个儿子形成 \(j\) 的空间,看着就比较背包,但由于从上往下 DP,需要想个办法扣掉 \(y\) 的贡献。于是可以用 回滚背包 实现,然后就没了。不知道考场能不能想清楚。
是不是把实现细节说完了?
P8935 [JRKSJ R7] 茎
tag:树形 DP。
有点困难?
正确思路
直观感受是先对着茎周围的子树背包一个 \(k - 1\) 然后继续考虑,然而问题在于前后分开考虑,不可,没准一个点就莫名其妙寄了两次。但是可以考虑 倒序模拟剪茎的过程,同时算上前面的贡献。为什么呢?因为发现加点是可以 逐步确定最后留下来的点数,并且计算上先前就有的删除方案,这就很好。
但一个问题是,计算的是操作序列,在从上往下 DP 时,每次只整出一个连续新点,贡献是好算的;倘若中间隔了一些点,怎么计算答案呢?发现似乎没有影响,只是 \(ans\) 变成一个 后缀和 而已,因为删掉的枝节 与先前的操作序列有双射(可以这么说吗),改变的只有后面减茎的方案数罢了,没有过多影响。
注意 \(x\) 处不可以后缀和,因为后缀和的意义是删掉这个点。剩下的细节依旧是自己想。
ARC087F Squirrel Migration
tag:重心,计数,容斥,猜结论。
错误思路
最优化加计数,考虑 猜个上界 构造一下。理想状态下,对于每条边,其内部的点 \(p_{i}\) 全都是外面的,这样保证边的收益最大化。如何构造呢?为了使每个子树 都能被外面的点填充,不出意外地人类智慧一把,钦定 重心 为根,于是每个子树 \(sz \le \dfrac{n}{2} \le n - sz\),往外面撒点就完事了。(虽然应该是式子推重心……)
由于重心的定义,上面那个还是个 充要条件。然后考虑直接计数。双重心是不是 \((\dfrac{n}{2}!)^{2}\) 就完了,那就只用思考单重心的求法了。是否需要容斥?若设不参与重排的 子树 集合为 \(S\),则贡献为 \((-1)^{\lvert S \rvert} \cdot (n - \sum_{i \in S} sz_{i})! \cdot \prod_{i \in S} sz_{i}!\)。这个式子的第二项 不能直接计算,所以设 \(f_{x, i}\) 为考虑到 \(x\) 时钦定了 \(i\) 个 点 不参与外界重排的 方案数乘系数,有 \(f_{x, i} = f_{x - 1, i} - f_{x - 1, i - sz_{x}} sz_{x}!\),最后计算 \(ans = \sum (n - i)! \cdot f_{m, i}\)。
正确思路
其实只有容斥寄了。每个点还是要单独考虑的,做完了。
CF724F Uniformly Branched Trees
tag:DP,计数,组合,重心。
错误思路
首先考虑 同构 怎么搞。由无根树同构的 trick,一般可以 钦定重心 来搞搞,于是需要分 单重心和双重心 两种情况讨论。先从单重心入手。
先浅浅的设个状态 \(f_{i, j}\) 为 \(i\) 个点,重心上连 \(j\) 条边,剩下都合法的方案数。然而 限制有点死,因为理论上重心外的点也可以向上连边,这就导致不方便计数,需要更多状态。考虑记 \(g_{i, j}\) 为在 \(f_{i, j}\) 的定义上留一个点连 \(d - 1\) 条边的方案数。然而还是很不能写转移式,因为在转移 \(f\) 的时候要 考虑子树大小的合法性。于是在 \(f\) 里再记一维最大子树大小?设个 \(f_{i, j, k}\) 试试:
其中 \(calc(i, j)\) 表示 \(i\) 个数里选 \(j\) 个构成可重集的数量,在 前缀和意义下 就是 \(j + 1\) 个空位插 \(i - 1\) 个隔板(因为第 \(i\) 个固定在 \(j + 1\) 的位置了),是不复杂的 \(\dbinom{i + j - 1}{i - 1}\)。这个可以预处理,大概 \(\mathcal{O}(n^{2} d)\) 做完了。
还有双重心的情况。双重心在分析时是不复杂的,可以简单看成 两个 \(sz = \dfrac{n}{2}\) 的树拼起来。但就是这个问题,会计重。怎么办捏?
正确思路
菜死了,其实只用一个 \(f_{i, j, k}\)。上述过程中的 限制太死了,导致会有情况没计到。实际上只用考虑树的 最终形态,因为重心的子树限制 只用关心一级儿子的大小,而不管儿子内部是啥妖魔鬼怪,所以最后一步之前的转移不需要考虑子树大小限制,然后完了。
problem:DP 状态太严或太死似乎都会有分讨过多的问题,如何分辨?
实现细节
差点又要写到思路里去了。
- 注意到 binom 里面,\(i - 1\) 很大而 \(j\) 比较小,故可以将其拆为 \(\dfrac{(i + j - 1)^{\underline{j}}}{j!}\),预处理就只用 \(\mathcal{O}(n^{2})\) 的总复杂度了。
CJ2002【2025省选模拟九】有缘人
tag:DP,长链剖分。
非常好分类讨论题,使我的 DP 式子旋转,爱来自细节。这是一个有关 Destiny 的小聋瞎对拍出奇迹的励志故事。
抽象思路
啥都记一下,太抽象了。首先是题面,两头的 四个枝节 长度一致,而非分别一致,不然后面的 DP 是做不了的。只能说致敬传奇硬做王(
第一眼,点分治;第二眼,不是点分治,因为点分治 会分割连通块,你又不知道 5 个部分哪个跨重心,别想了。
于是考虑进行一个类的分。一定不要漏情况! 顺便说一下 无根树和有根树 的关系——有根树相当于在无根树的基础上 加入限制,或者说 增加信息,那么一些无根树上不好区分的情况,就可以在有根树上区分,方便分讨。根据 \(\infty\) 次的 暴力对拍 可以得到如下情况:
初步想法是,直接按照类别 DP,比如 I. 看成两个点对凑、IV. 看成一个 F
型然后判断上面接法。由于这些状态与 深度强相关(比如需要记录 \(k\) 级儿子数量),考虑用 长链剖分 优化。然而写一写就会发现,不好转移。问题不在长剖,在于 VI. 和 VII. 的情况在分界点是 考虑不到的,以及这么干的话对于 IV. 会出现 长链的答案不能直接继承,需要扫一遍数组,复杂度会炸的情况。
这种情况的根源在哪呢?其实是 代表元 找错了。在有根树 DP 计数上,更为常见的 trick 是 钦定在最高点上计数,因为这样就只需要关心 子树拼接计算答案,符合 DP 获取信息的顺序。因此再从这方面考虑,简化分讨。
经过观察整合,其实就剩了两类:
这就一副很方便计数的样子。由于计数对象的 特点 或者叫 性质 在于四个枝节等长,DP 就可以经典地设为:
- \(f_{x, i}\):下接一条长为 \(i\) 的链的方案数,就是 \(i\) 级儿子,转移时长度递增;
- \(g_{x, i}\):下接一个人字,枝节长为 \(i\) 的方案数,这里就可以把在 \(x\) 处分岔的情况并上来,免掉分讨,转移时长度固定;
- \(h_{x, i}\):下接一个
F
,可能出头,还需要接 \(i\) 的长度的一条链的方案数,这个是顶头分岔的情况,转移时长度递减。
对应的转移有:
需要注意顺序和边界,有点恶心。题解写了个「极其美妙」的多项式转移,据说 DP 的本质就是 模拟多项式乘法。有动态 DP 那味儿了。
实现细节
- 主要是鬼畜的 空间分法,这个需要依赖长剖过程中的 继承方式。\(f\) 是规规矩矩的长剖,不用管;\(g\) 由于长度 不变,同一条长链 共享一段 \(len = dep_{top} - d_{top}\) 的空间;\(h\) 这个 B 玩意儿是递减的,需要倒序分空间,同时注意链头的数据仍可以达到 \(len = dep_{top} - d_{top}\) 的情况,所以 \(aux\) 数组需要开两倍空间。