chang-pou

长剖

一般是优化 dp 用的。

比如说,求每个点子树内,某个深度的所有点的某些信息。(\(max, min, sum\) 等等)

假设现在要求,对每个点求子树内距离它为 \(k\) 的所有点点权和。\(k\) 不定。

可以考虑一个 \(n^2\) dp,设 \(f_{u,i}\) 表示 \(u\) 子树内距离 \(u\)\(i\) 的所有点点权和。

转移就是,\(f_{u,0}=a_u\),剩下的 \(f_{u,i}=\sum_vf_{v,i-1}\)

我们注意到,假设 \(mx_u\) 表示 \(u\) 子树内到 \(u\) 距离最大的点的距离,\(sec_u\) 表示 \(u\) 子树内到 \(u\) 距离次大的点的距离。

则发现 \(f_{u,(sec_u,mx_u]}\) 算的点都是唯一的,就是长链上末端的点。

\(son_u\) 表示 \(u\) 的重儿子(或者叫长儿子),那么我们可以这样 dp:

dp 到 \(u\) 的时候,从 \(f_{son_u}\) 部分直接继承信息,对每个剩下的儿子 \(v\) 枚举 \(v\) 所有的深度,即 \(mx_v\),暴力转移。

一种写法是,对每个 \(f\) 开一个反过来的 vector \(f'\)\(f'_{u,i}\) 存原来的 \(f_{u,mx_u-i}\)

这样可以直接从儿子继承,即 \(f'_{u}\) 先等于 \(f'_{son_u}\),其他儿子再暴力转移。

这样很不好写!有一个非常好写法,爱来自cyf:

dp 到 \(u\) 的时候,把 \(f_{u,i}\) 存在 \(u\) 所属长链的 \(u\) 向下走 \(i\) 格的点上。

这个很牛,这样从重儿子继承就啥都不用改。轻儿子暴力转移的话,就枚举一个轻儿子 \(v\),从 \(u,v\) 的长链同时开始走,走到的 \(u',v'\)\(f_{u'}\gets f_{u'}+f_{v'}\) 即可。

然后复杂度还没证,我们会发现这样每次暴力转移,每个轻儿子 \(v\) 会产生 \(mx_v\) 的贡献。

这些轻儿子一定都是长链链顶。所以等于说每条长链都会在链顶的父亲处被计算长度次。

而所有长链长度之和是 \(n\)。所以复杂度 \(\Theta(n)\)

看一个题:link

考虑枚举路径的 \(lca\)\(t\)。现在如果只有这一个点,答案是 \(g_t\)\(g_t\) 为所有点到 \(u\) 的距离之和。

如果路径某一端 \(u\) 拓展了一个点 \(v\),那么答案会变小 \(w(u,v)\times siz_v\)。假设选了 lca 子树内两点 \(u,v\) 作为路径端点,那么答案就是 \(g_{t}-\sum_{i:t\to u}w(i,fa_i)\times siz_i-\sum_{i:t\to v}w(i,fa_i)\times siz_i\)

\(s_u=\sum_{1\to u}w(i,fa_i)\times siz_i\)。则上式就是 \(g_t-(s_u-s_t)-(s_v-s_t)\)

也就是说,要在子树 \(t\) 内选两个不同子树的点 \(u,v\),满足到 \(t\) 距离之和为 \(k-1\),且 \(s_u+s_v\) 最大。

考虑 dp,设 \(dp_{u,i}\) 表示 \(u\) 子树内距离 \(u\)\(i\) 的点的点权最大值。

那么枚举其中一个的深度 \(i\),另外一个就是 \(k-1-i\),两个最大值相加即可。

注意到有来自不同子树的条件(即两条路径不相交)。那么可以将儿子 \(v\) 的信息合并进去前算一下 \(v\) 的所有深度的贡献,即在之前已经转移好的 dp 数组里求一下 \(k-1-i\) 深度的最大值。

长剖优化一下,转移还是合并轻儿子前算一下贡献。注意考虑 \(u,v\) 中一个等于 \(t\) 的情况。

复杂度 \(\Theta(n)\)

posted @ 2024-02-29 07:55  iorit  阅读(24)  评论(0)    收藏  举报