树的直径.
记树的某条直径为 $S-T$ ,则对于一个点 $x$ ,能对它造成贡献的点,一定都在 $x$ 到 $S,T$ 中较远的那个点的路径上.
否则,若还有其他点能对 $x$ 造成贡献,那么它到 $x$ 的距离一定大于 $x$ 到 $S,T$ 的距离,可以构造出更长的直径.
以 $S$ 为根做一次 dfs ,对每个 $x$ ,统计一下 $S\to x$ 路径上有几个点能对 $x$ 造成贡献,再以 $T$ 为根做一次同样的 dfs .
对于同一个 $x$ 的两次 dfs ,假设 $x$ 到 $S$ 更远,那么 $T$ 那一次的 dfs 计入贡献的点就是 $S$ 那一次计入贡献的点的子集.
那么把两次的答案取 $\max$ 就是实际的答案.
dfs 时,用一个栈维护根到当前节点路径中,能造成贡献的点,开一个桶记录每种权值出现次数.
往栈中加点或者删点的时候,更新桶,并且更新当前答案,类似于莫队增加或删除端点时的操作.
假设当前已经处理了 $x$ 的答案,需要继续向下 dfs .
为了保证栈中点都是有贡献的,要先把栈中与 $x$ 距离小于等于 $x$ 到其它子树中最深的点的所有点删掉.
先往最深点的方向走,再往其它儿子走,删除操作就不用被撤销掉了.
每个点只会对它的父亲贡献 $O(1)$ 次进出栈操作,时间复杂度 $O(n)$ .
1 | //%std |