有正能量的动态规划
\(12.22\) 有正能量的 \(\text{DP}\) 之 \(\text{TTRREEEE}\)
树的价值
首先需要把整个问题刻画成为能够 \(\text{DP}\) 的形式,那么就需要一些性质和转化。
- 最终答案的树一定是一个大根堆。
如果不是大根堆,那么设 \(u,v\) 满足 \(v \in \text{subtree}(u),a_u<a_v\),那么交换 \(a_u\) 和 \(a_v\) 一定不劣。
接下来一个很直接的想法是,令 \(a_u=\max_{v \in \text{son}(u)} a_v+1\),可是这样的策略并不优,因为 \(a_u\) 可以选择更大的值,这样虽然会减少自己的贡献,但是或许可以将他到根的这条链上的更多点的贡献 \(+1\)。
赛时做这个题的时候心态已经完全爆炸了,所以想到这里就进行不下去了,最后指数级暴力获得 \(8 \text{pts}\) 呜呜呜呜呜。
上面策略不优的原因其实就是当前点的贡献可能会在更高处产生,所以可以考虑贡献延后计算。
于是可以设 \(f_{u,i,j}\) 表示在以 \(u\) 为根的子树中,当前的 \(\text{mex}=i\),且还有 \(j\) 个点未确定权值,直接转移可以做到 \(O(n^3)\) 获得 \(48\) 分。
进一步观察 \(\text{DP}\) 的过程,发现贡献来源于两个部分,一个是用在当前点的“垫脚石”,另一个是继承的儿子的 \(\text{mex}\)。
注意到垫脚石所产生的贡献,会沿着继承关系一直上传,换句话说,我们可以不用考虑继承这件事,直接看成垫脚石对某一条链上的所有点都造成了贡献即可。
考虑先钦定一种继承关系,发现这是一个树链剖分的过程,然后对于每个点,其贡献即为它到根上最长的重链的长度。
于是有状态 \(f_{u,i,j}\) 表示钦定点 \(u\) 到根最长重链长度为 \(i\),点 \(u\) 到当前所在重链的顶端距离为 \(k\),\(u\) 子树内贡献最大值,直接转移便能做到 \(O(nm^2)\)。
发现复杂度瓶颈出现在了状态上,根本原因是链剖分的所有形式都被我们考虑了进去,考虑是否存在一些链剖分的方案是一定不优的。
经过手玩可以发现当 \(1<j<i\) 时,这样的剖分方案是一定不优的。
于是可以只记录两种状态 \(f_{u,i}\) 和 \(g_{u,i}\) 分别表示上述的 \(j\) 取 \(1\) 和 \(i\) 时的答案。
\(g\) 的转移依旧是简单的,但是 \(f\) 的转移需要去枚举一条链:
注意到链上的贡献可以用树状数组处理,容易做到总复杂度 \(O(nm \log_2 m)\),可以通过。
最大权独立集问题
\(\text{Part I}\)
这道题的切入点在于考虑子树内和子树外唯一的一次交换,如果没注意到这一点就只能写 \(O(n!)\),或者转移比正解还复杂的 \(O(n^4)\)。
于是不难得出 \(f_{u,p,q}\) 表示子树 \(u\) 以及 \(u\) 到其父节点的所有边删光,\(u\) 和其父节点交换时,\(u\) 节点上的值是 \(p\),父节点上的值是 \(q\)。
每次转移直接 \(3!\) 枚举点 \(u\) 的三条邻边的删除顺序,可以做到 \(O(n^3)\)。
发现复杂度来源主要是枚举 \(q\),于是考虑将 \(q\) 改为子树内的东西,设 \(f_{u,p,q}\) 表示 \(u\) 子树所有边删光,\(p\) 从 \(u\) 子树交换出去,从子树外换进来的点最终位于 \(q\)。
转移可以考虑贡献延后计算,即在确定了 \(q\) 的值的时候再计算其代价,结合一些优化,总复杂度即为 \(O(n^2)\)。
这也太神秘了?!完全想不到第一步,完全想不到第二步,完全想不到第三步!!!有没有更像人类的思考方法??有的有的。
\(\text{Part II}\)
-
元素在图上或者树上进行移动或者交换等过程,可以直接考虑每个元素最终移动到了哪里,即刻画一个排列 \(p_i\) 表示点 \(i\) 上的元素移到了 \(p_i\)。
-
分步转移,在变量较多时,可以先去枚举其中一部分,需要满足这一部分变量在被转移项的下标上的任何一次出现的形式都相同。并处理与这一部分变量相关的贡献存下来,那么在这之后,这若干个变量就可以看作是一个变量进行枚举了,本质是空间换时间。
设原本在点 \(i\) 上的点权经过移动,最终到达了 \(p_i\)。
那么考虑一开始的时候 \(p_i=i\),每一次交换会合并两个置换环,那么最终一定只剩下一个置换环,同时,每一条边两端的点集在环上都是连续的一段。
必要性是显然的,考虑充分性,可以根据环上的点的顺序构造出一棵合法的树的 \(\text{dfs}\) 序来证明。
那么状态设计就很简单了,因为一棵子树上的点在最终的环上是一条链,所以直接记录其链头和链尾即可,发现实际上就是 \(f_{u,x,y}\) 表示 \(u\) 子树所有边删光,\(x\) 从 \(u\) 子树交换出去,从子树外换进来的点最终位于 \(y\)。和上文的最终状态是一样的。
直接分讨转移可以做到 \(O(n^3)\),观察转移式容易发现可以斜率优化,不过太麻烦了。更简单的优化是注意到贡献式子里的每一项最多和两个枚举的量有关,于是可以分步转移做到 \(O(n^2)\)。
\(\text{Not Another Constructive Problem}\)
这道题和上一道题有类似的结论。
依然可以刻画成点 \(i\) 最终移动到了 \(p_i\),那么依然有上文中合法的充要条件,由于需要计数,我们还需要边的连接情况,考虑把边都放在环上观察。
进一步地,由合并的过程我们还可以发现,子树连续的条件等价于边在环上都不交,有了这两个关键性质之后,可以考虑区间 \(\text{DP}\)。
按照区间 \(\text{DP}\) 的惯用流程,对于区间 \([l,r]\),寻找一个点 \(mid\),使得没有边会跨越 \(mid\),即可简单的实现转移,若存在多个合法的 \(mid\),钦定找最靠左的一个 \(mid\) 即可。
考虑什么情况下会找不到 \(mid\),显然当且仅当 \(l\) 和 \(r\) 有边时才找不到,这种情况是好转移的。
那么就有状态 \(f_{l,r}\) 表示 \([l,r]\) 连成一棵合法树的方案数,\(g_{l,r}\) 表示 \([l,r]\) 连成一棵合法树且 \(l\) 和 \(r\) 有边的方案数,直接转移即可做到 \(O(n^3)\)。
\(\text{Agenci}\)
考虑划分为 \(k\) 个连通块使得每个块恰有一个关键点 \(p\),然后在每个连通块里选出以 \(p\) 为一端的最长链,要使得所有链的总长度最大。
稍加思考,可以发现其实就是以每个关键点为起点选出若干条链,要使得任意两条链不交,且总长度最大。
这种问题一般直接设 \(f_{u,0/1/2}\) 表示以 \(u\) 为根的子树内,点 \(u\) 周围有 \(0/1/2\) 条边被选了的最大值。特别地,如果 \(u\) 是关键点,认为 \(u\) 向下选出一条链的方案记录在 \(f_{u,2}\) 中。
根据转移需要,再设 \(g_u\) 表示以 \(u\) 为根的子树内,有一条以 \(u\) 为起点向下的链被子树外的关键点所选择的最大值。
总时间复杂度显然为 \(O(n)\)。
树数叔术
- 分步转移。
这道题很重要的树合法的充要条件是,\(\forall i \in \{0,1,2,\dots,V\}\),满足由权值 \(\leq i\) 的点构成的虚树上,要么权值 \(=i\) 的点仅有一个,要么权值 \(=i\) 的点均不为叶子。
理解起来比较容易,但是注意到这个结论非常困难。
不想写怎么证的了,因为根本注意不到。
那么只需要根据该结论,按权值从小到大地在虚树上加入点即可。加点的方式共有 \(4\) 种。
-
挂在某一个已有的点上,作为新的叶子。
-
挂在某一条已有的边上,作为新的叶子,并产生一个为确定权值的点。
-
放置在某一条已有的边上,作为一个非叶节点。
-
给一个未确定权值的点赋值,不新增任何点。
可以发现,要维护这个过程,需要类似于延后决策地记录当前虚树上未确定权值的点的个数,所以有状态 \(f_{c,i,j}\) 表示目前确定了权值 \(\leq c\) 的所有点,虚树上共有 \(i\) 个点,其中有 \(j\) 个点还未确定权值。
直接对上面四种情况分别讨论转移即可做到 \(O(n^5)\),注意到可以分步转移,总复杂度降至 \(O(n^4)\)。
\(12.16\) 有正能量的 \(\text{DP}\) 之状压和容斥
主旋律
-
强连通分量相关考虑缩点。
-
\(\text{DAG}\) 容斥。
这是一道 \(\text{Gay}\) 佬题。
有向图上计数状物首先考虑强连通分量缩点,那么合法条件即为缩点后整个图有且仅有一个 \(\text{scc}\)。
设 \(f_S\) 表示将 \(S\) 的导出子图经过删边和缩点之后仅剩下一个 \(\text{scc}\) 的方案数,套路地,考虑用总方案减去 \(g_S\),\(g_S\) 表示缩点后的 \(\text{DAG}\) 的点数 \(>1\) 的删边方案数。
其中 \(e(S,T)\) 表示连接点集 \(S\) 和 \(T\) 的边数。
另外再设 \(h_S\) 表示缩点后 \(S\) 划分成若干个 \(\text{scc}\),其中任意两个 \(\text{scc}\) 之间都没有边的方案数。
于是可以根据 \(\text{DAG}\) 的特点,每一次枚举入度为 \(0\) 的点集进行转移。
进行这一步转移的时候要求 \(h_S\) 不包含 \(f_S\),事实上也包含不了。
但是这显然会算重,原因就是 \(\text{T}\) 不一定就是最终的 \(0\) 度点集合,可能只是它的一个子集。
所以考虑容斥,直接钦定 \(T\) 是 \(0\) 度点集合,然后容斥出正确结果。
这里的容斥系数推导和缩点之后大点集合有关,所以下面的集合均表示缩点后的大点集合。
\(F_S\) 表示钦定 \(0\) 度点集为 \(S\) 时的答案,\(F'_S\) 表示 \(0\) 度点集恰好为 \(S\) 时的答案。
所以容斥系数大概就是 \(-1\) 的缩点后点数 \(+1\) 次方。
还有一种二进制反演的推导方式:
\(F_i\) 表示钦定 \(0\) 度点集大小为 \(i\) 时的答案,考虑 \(0\) 度点集大小恰好为 \(i\) 时的答案会被计算多少次,令 \(h_i\) 为 \(F_i\) 的容斥系数。
然后注意到 \(h_j=(-1)^{j+1}\) 就可以了,并不是很通用。
重塑时光
-
找合法的充要条件。
-
\(\text{DAG}\) 容斥。
-
卷积形式的转移可以考虑成多项式乘法的形式,然后利用点值转移,最后插值出答案即可。
大概就是主旋律的小幅加强版。
若干个连续段合法的条件就是每一段都需要是其生成子图的拓扑序,然后每一段内的所有点缩成一个大点之后在原图上仍然是 \(\text{DAG}\)。
那么就考虑直接在 \(\text{DAG}\) 上对于缩点方案做 \(\text{DP}\),每种缩点方案最后贡献的系数显然只和缩点后的点数相关,是容易计算的。
那么整个题目就类似与对缩点后的大点去做 \(\text{DAG}\) 计数,和主旋律很类似,套过来就好了。
最后 \(O(3^n n^2)\) 的转移是卷积形式,考虑直接写成多项式的系数,然后存 \(O(n)\) 个点值,这样卷积就可以 \(O(n)\) 算了。
\(\text{Harry The Potter}\)
-
找合法的充要条件。
-
选两个点进行一些操作可以看作是图上的一条边。
-
一些类背包问题可以考虑折半搜索。
首先是两种操作交替进行太混乱了,因为顺序显然不影响结果,所以先考虑操作 \(2\),然后考虑操作 \(1\)。
对 \(i\) 和 \(j\) 进行操作 \(2\) 看作是它们连了一条边,那么会形成若干连通块,对于一个连通块 \(S\),如果其内部边数 \(\geq |S|\),换成 \(|S|\) 次操作 \(1\) 显然不劣。
那么最优方案中每个连通块均为树,进一步地,如果操作完之后,树上仍然存在非 \(0\) 点,那至少要进行一次操作 \(1\),此时把该连通块的操作 \(2\) 全换成操作 \(1\) 也是不劣的。
所以题意转化为将 \([n]\) 划分成最多个不交的子集 \(S\),使得 \(S\) 内的点可以用 \(|S|-1\) 次操作 \(2\) 清零。
考虑存在合法操作方案的充要条件,不难发现是 \(sum_S \equiv |S|-1 \pmod 2\),且存在 \(T\),满足 \(T \subsetneqq S,T \not= \emptyset,|sum_T-sum_{S-T}| < |S|\)。
直接对这个暴力做有两部分复杂度,一部分是对于所有 \(2^n\) 个集合是否合法的判定,另一部分是求答案的 \(\text{DP}\)。
前一部分显然类似于背包问题,并没有多项式复杂度算法,不过可以折半搜索,具体地,分成两半后每个物品有正负两种贡献,在搜索过程中归并排好序,最后双指针找答案即可。
总复杂度为 \(O(3^n + (\sqrt 2 + 1)^n)\)。
绝顶之战
-
\(\text{DP}\) 的值域为 \(0/1\) 或者较小的时候可以考虑交换 \(\text{DP}\) 的状态和值域。
-
\(\text{DP}\) 转移中的贡献或者限制有后效性,但是贡献和限制只和最后的整体形态有关,那么就可以提前对整体形态的某个量进行枚举,算答案时仅计算符合枚举条件的部分即可。
首先有 \(dp_{S,i}=0/1\) 表示若总长为 \(i\),仅成功放入 \(S\) 集合内的物品能否做到,那么就要求 \([n]-S\) 内的物品不能被放入。
但是注意到,转移过程中要求不被放入的物品集合并非一直是 \([n]-S\),换句话说就是有后效性。
此时直接将有限制的物品集合记录到状态中是可行的,只是空间复杂度会更高。
更优秀的做法是,由于限制仅和最后要访问的是哪个集合有关,所以考虑直接枚举有限制的集合再去 \(\text{DP}\)。
但是单次 \(\text{DP}\) 的复杂度依旧很高,\(\text{DP}\) 的值域是 \(0/1\) 能否交换状态和值域?
注意到对于一个 \(S\),满足 \(dp_{S,i}=1\) 的 \(i\) 是一段区间 \([\sum_{j \in S} a_j,x)\),直接设 \(f_S\) 表示这个 \(x\) 即可,总复杂度就优化到了 \(O(4^n)\)。
\(\text{Just Long Neckties 2}\)
- 交换 \(\text{DP}\) 的状态和值域。
如果不用 \(\text{Dilworth}\) 定理转化可以直接得到状态。
用了 \(\text{Dilworth}\) 的话,再用一次 \(\text{DP of DP}\) 的技巧也能得到状态。
大概就是能设出来 \(f_{i,S}=0/1\) 表示当前考虑了 \(1 \sim i\),链头集合为 \(S\),且第 \(i\) 个必选是否可行。转移直接考虑加入 \(a_{i+1}\) 或者 \(a_{i+2}\)。
状态数过于巨大了,但是注意到值域很小,可以考虑交换状态和值,把 \(S\) 交换过去不太行,因为不好说哪个 \(S\) 是最优的。
所以就设 \(g_S\) 表示使得 \(f_{i,S}=1\) 的最大的 \(i\) 是多少,这时转移需要先处理自环,可以预处理一些东西来做,也可以二分结合数据结构做。
最后时间复杂度可以做到 \(O(2^V V^2+nV)\) 或者 \(O(2^V V \log_2 n+nV)\)。

浙公网安备 33010602011771号