「树形结构 / 树形DP」总结

Codeforces 686 D. Kay and Snowflake

要求$O(n)$求出以每个节点为根的重心。

考虑对于一个根节点$u$,其重心一定在【各个子树的重心到$u$】这条链上。这样就能够$O(n)$推出来了。证明起来难证易忘。不如记住树的重心的几条奇妙性质:

1. 以重心为根,各子树大小都不超过树的一半。

2. 已知一子树$v$的重心为$x$,则$v$父节点$u$的重心一定在$(x,u)$这条链上。

... 下次用到了再说

Codeforces 842 C. Ilya And The Tree

这是一道玄学的题目。可以根据每个点清不清0转移状态。如果该点清0,那么很容易,父亲一直到根节点都不允许清0了。如果该点清0,是不是能直接转移呢?并不行,这里我犯了一个错误,并不是先前答案越大,更新得到的当前答案也会更大。换句话说,不满足最优子结构的性质。因此我们需要用一个数据结构维护父亲节点到根之间有且仅有一个点不选时的所有状态。这样的状态不会多,因为一个数被更新的状态时很有可能重复的。当然,复杂度还是很玄学。

感觉这道题主要是为了练set……

Codeforces 813 C. The Tag Game

给出一棵树和两个人。一人从$x$开始逃,一人从根开始追,问最长能追多长时间追到(每秒一条边)

考虑x要么直接往下逃,要么往上走一段距离然后往下逃。一旦开始往下逃并且没被抓,就会逃到底。因此只需要维护每个点的高度和深度,然后枚举往下逃的点(一定在到根的链上)更新答案即可。

其实这道题目没有必要规定根。不规定根答案也是一样的。

Codeforces 337 D. Book of Evil

给出一颗树上的$m$个标记点,求有多少个树上的点满足距离所有标记点都小于等于$d$。

距离所有标记点都要小于等于$d$,可以转化为距离最远的标记点要小于等于$d$。由此问题转化为求出每个点到距离它最远的标记点的距离。

考虑使用树形DP。对于一个点$u$,我们可以把最远的标记点划分为几个区域。一,$u$的子树内;二,$u$的子树外。而$u$的子树外的又可以分为两个部分:$u$的父亲的子树外,$u$的父亲的子树(不包括$u$所在的)。前者可以直接转移,后者是子树问题(注意要维护次大值,因为有可能与$u$的子树重合)。

注意要特判没有标记点的情况!

Codeforces 219 D. Choosing Capital for Treeland

给出一棵树,给每条边规定一个方向。要选择一个点,按照边的方向走能到达所有点。可以花1的代价改变一条边的方向。问最小代价。

用树形DP来思考问题那么就只需要考虑父子之间的关系了。先DFS预处理出从根节点$rt$到所有点的代价。然后考虑如果要将根节点的一个子节点$u$作为起点,那么只要考虑$(rt,u)$这条边的方向就好了。至于如何将初始边的方向存下来,我们可以考虑给边一个权值。0代表是原始方向,1代表这是反向边。

Codeforces 212 E. IT restaurants

给出一棵树,规定一条边的两端要么染不同颜色,要么一端不染颜色。总共两种颜色,一个点最多只能染一种颜色。两种颜色都必须染至少一次。问最多有多少个能被染色。并输出在最大化的情况下,两种颜色的个数分别有哪些情况。

第一问显然$n-1$,也就是一个点不染作为过渡,其它全染。我们可以把这个不染的点看做根节点。那么各个子树内的颜色必定是一样的,转化为01背包问题。

枚举每个点,求出各个子树的大小。做一次背包。我们来考虑复杂度是多少。设每个点的访问的子树个数是$k_i$,那么时间复杂度是$O(\sum\limits_{i=1}^{n}nk_i)$,我们发现每条边恰好被访问两次,因此$\sum\limits_{i=1}^{n}k_i=2n$。因此复杂度是$O(n^2)$

Codeforces 161 D. Distance in Tree

求树上距离为$k$的点对个数($n \leq 50000, k \leq 500$)

显然要维护$f_{u,k}$表示$u$的子树内有多少点到$u$的距离为$k$。一个做法是每次在枚举两棵子树,再枚举$k$进行转移;或者利用树形背包;但这样的复杂度都不可行。

子树内做不了考虑向上走,这是我没有想到的。刚才的做法之所以不可行,是因为起点和终点都未知,导致增加了子树选择的枚举。不如枚举起点,然后暴力往上走$k$次进行转移。即$dp_u = \sum\limits_k f_{g,K-k}$。注意$f_{g,K-k}$包含了$(u,g)$这条路径上的,因此要减掉。所以$dp_u = \sum\limits_k f_{g,K-k}-f_{las,K-k-1}$.

然而一般做法是点分治……

[NOIp2018] Day1T3 赛道修建

给出一棵树,每条边最多只能属于一条赛道。问要修建$m$条赛道,最短赛道最长是多少。($n \leq 5*10^4$)

二分答案。问题转化为判定每条赛道长度都大于等于$mid$时能否构成$m$条或以上赛道。考虑对于任意一个节点$u$,经过它的赛道要么在子树内,要么从下面穿过它并向上延伸。我们发现以下说法是正确的

  • 能从下面穿过它的最多一条,否则就会在上面重叠;
  • 最多只能用子节点的两条延伸来拼赛道,不然不符合题意;
  • 如果明明能通过$u$在子树内构成赛道,不必将它拆掉而向上延伸,因为贡献都是1(这是个贪心);
  • 如果将u作为起点向下延伸能构成一条赛道,那么不必用它去跟别人拼成一条赛道,因为贡献都是1(这也是个贪心);
  • 在子树内凑出同样数量的赛道肯定越短越好,尽量留出长的贡献给上面。

于是我们维护一个节点的所有子节点中延伸上来的长度(没有向上延伸就是0),优先选择$\geq mid$的点直接作为一条赛道;然后开始用两条来拼——从最小的开始考虑,二分出一个能到达$mid$的最小值,拼一下,然后以此类推。

复杂度$O(nlog^2n)$

思想又难细节又多。真恐怖!

Codeforces 1153 D. Serval and Rooted Tree

给出一棵树,每个非叶节点有一个操作$min$或$max$。要求在总共的$k$个叶子上填互不相同的$1$~$k$的整数。非叶节点的值为对其子节点的值进行对应操作得到的值。求根节点可能的最大值。

好像看似每个子树不是独立的,而子树的根节点能达到的值在子树的所有叶节点中的最大排名确是可以独立的。事实上题目规定填$1$~$k$的整数是没有必要的,只要知道根节点能达到的值在所有叶节点中的排名就可以了。于是我们可以考虑维护$dp_u$表示子树$u$中根节点所能达到的最大排名。我们利用子节点来推父节点。

考虑最大值。设目前考虑节点$u$,节点$i$的大小是$sz_i$。$u$有$k_u$个子树。每一个子树有一个叶子数,可以想象一下——有$k_u$个队列起初是空的,我们要从大到小将数推入这些队列。对于子节点$v$,$dp_v$之前的部分是不可取的。问可取的部分中每个队列的最大值的最大值是多少。显然我们优先将数全部填入$dp_v$最小的那个队列里,就能获得最大排名$dp_v$了。由此$dp_u=min\{dp_v\}$

考虑最小值。我们要让各个队列的最大值中的最小值尽量大,也就是要尽量平均。我们无论如何先得把每个队列中取不到的部分填满。然后依次在每个队列填一个数。最小的那个就是最大值的最小值了。由此$dp_u=\sum\limits dp_v$

Codeforces 14 D. Two Paths

给出一棵树,边权为1,求两条点不相交的路径的长度乘积的最大值。($n \leq 200$)

正解很简单:枚举边,删掉,两边求直径。事实证明我实在想得有点多。这类问题可以作为一个模型——我们可以将没有交集的两个东西当做独立的来分别求解。

Codeforces 1156 D. 0-1-Tree

给出一棵树,边权为0或1。问有多少点对$(x,y)$满足其路径上边权单调递增。($n \leq 2*10^5$)

对于合法的点对,其间路径有三种情况:全0;全1;前半部分0后半部分1。我们可以将树按0与1缩点(边两端点的点都要缩进去),用并查集维护大小。考虑单独点内也就是所有的全0或全1的情况了。对于既有0又有1的情况,考虑一定有这个转折点,而这个转折点一定同属两个并查集。减一后(交点不算)相乘即可。

注意并查集在访问祖先的时候不能直接使用$f_u$,因为可能还没有进行路径压缩。必须要使用$find$。

 Codeforces 1092 F. Tree with Maximum Cost

给出一棵树($n \leq 2*10^5$),有点权$a_i$,无边权。从$u$走到$v$代价$f_{u,v}=a_u * dist_{u,v}$。请选择一个$v$,使得$\sum\limits_{u}f_{u,v}$最小。

类似的题目以前做到过。首先这个代价的意义先理解一下:可以认为是从$u$出发到$v$上每条边的边权都是$a_u$。由此,我们可以先通过树上差分状物求出根节点的答案。然后由根节点向下推——每次令推的方向上那颗子树上所有路径缩短,其余全部增加即可。

 

总结

树形DP——先设定状态,然后考虑状态的转移。一般是由子节点推父节点,也有特殊情况是由父节点推向子节点,看题目怎么问。甚至还有的是子节点先推出根节点,根节点再推回来。注意分析复杂度,一般如果是直接转移的,那么时间复杂度就是状态数;也可以用$\sum\limits k_i=n-1$(边数)来理解。常见的要维护的信息有:深度,叶子数,离根最近的叶子深度。我们把不影响当前状态的东西看做一个整体来处理,这样思路比较清晰。有时候也可以把某些问题类比为较为熟悉的线性问题。

posted @ 2019-05-27 09:32  DennyQi  阅读(736)  评论(0编辑  收藏  举报