P7897

problem && blog

第一道正经的 Ynoi,特此写篇题解纪念一下。


Algorithm 1

可以想到 \(O(nm)\) 的 DP。

我们定义 \(dp_u\)\(u\) 子树内并包含 \(u\) 的连通点集,权值之和最大。

所以就有 DP 式啦!!

\[dp_u = a_u + x + \max(0,dp_v) \]

Algorithm 2

我们需要考虑优化。

既然加上 \(dp_v\) 的条件为 \(dp_v \ge 0\)。那么我们可以把不符合这个条件 \(v\) 与他的父亲节点 \(u\) 的边删掉,到 \(dp_v \ge 0\) 时把 \(v\)\(u\) 连起来。

所以我们的 DP 转移就变成了:

\[dp_u = a_u + x + dp_v \]

如果我们把 \(x\) 从小到大排序,那么 \(dp_u\) 就一定是单调递增的。

所以 \(\max(0,dp_u)\) 是取 \(0\) 还是 \(dp_u\) 指只会变一次。

所以 \(u\)\(v\) 的联通关系也只会改变一次。

所以我们如果可以的时候只需要在 \(dp_v\) 大于 \(0\) 时从 \(v\) 往上走,如果可以联通就连上。

此时我们需要计算 \((u,v)\) 这一条边在 \(x \ge\) 时联通。

所以我们就可以列出方程:

\[siz \times val + sum \ge 0 \]

解出 \(val \le \left \lceil \dfrac{-sum}{siz} \right \rceil\)

其中 \(sum\) 为子树和,\(siz\) 为字数大小。

所以我们就可以把所有的 \(\left \lceil \dfrac{-sum}{siz} \right \rceil\) 扔到一个堆里。每次取出最小元素看是否小于等于当前的 \(x\),如果小于等于就修改。

所以我们只需要一个数据结构维护以下几种操作:

  1. 加边.

  2. 将某个点到根的路径修改。

  3. 单点查询。

我们做出来的新树中某个点到根的路径在原树中就是一条链。

所以其实我们可以忽视这个动态加边,只需用并查集维护根节点即可。

对于 \(2,3\) 操作,树剖肯定会超时,所以我们考虑树上差分。

查询时,只需查询这个点子树内所有点权和。这样就可以 \(dfs\) 序 + 树状数组维护即可。


code

posted @ 2023-10-03 14:59  sqrtqwq  阅读(19)  评论(0编辑  收藏  举报