1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alex_Wei
**搬运于
2025-08-24 22:15:33,当前版本为作者最后更新于2021-11-03 16:33:02,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
摘自 贪心专题 第三部分例题 I.
究极神仙题。
首先对问题进行转化:相当于我们需要求原树最浅的一个点分树深度。假设点 在倒数第 次被问到,那么任意两个 值相同的点 之间的简单路径必然存在一个点 使得 ,因为这样才能使它们进入不同的点分树子树,说人话即 是深度相同的点 在点分树上的 LCA(的祖先)。
考虑这样一个贪心:我们记 表示 的子树内所有可能不满足条件的 值,即存在 使得不存在 满足 的所有 的集合,以二进制状压形式存储。合并 的两个子树 时,首先 应大于任何一个既在 又在 内的元素,否则显然不合法。此外, 还不应存在于 和 中,我们选择所有满足条件的最小的 即可。 即所有 与 的并去掉所有 的元素后的集合。
$$d_i=\min \{c\mid c>\max\{x\mid x\in S_u\cap S_v\}\land c\notin S_u\}\\ S_i=\{d_i\}\cup\left\{x\left|\ x\geq d_i\land x\in\bigcup_{u\in\mathrm{son}(i)}S_u\right.\right\} $$根据点分树的结论答案不可能超过 ,因此时间复杂度 ,可以做到线性:通过位运算我们求出可行的 集合,取 即可,拿到了最优解。
const int N = 5e4 + 5; const int K = 1 << 16; int cnt, hd[N], nxt[N << 1], to[N << 1]; void add(int u, int v) {nxt[++cnt] = hd[u], hd[u] = cnt, to[cnt] = v;} int n, f[N], S[N], lg[K], buc[K]; void dfs(int id, int fa) { int msk = 0, ban = 0, leaf = 1; for(int i = hd[id]; i; i = nxt[i]) { int it = to[i]; if(it == fa) continue; leaf = 0, dfs(it, id), f[id] = max(f[id], f[it]); msk |= ban & S[it], ban |= S[it]; } if(leaf) return S[id] = 1, void(); msk = (K - (1 << lg[msk])) & (K - 1 - ban); int c = !msk ? lg[n] + 1 : buc[msk & -msk]; f[id] = max(f[id], c); S[id] = (ban & (K - (1 << c))) | (1 << c); } int main(){ cin >> n; for(int i = 2; i < K; i++) lg[i] = lg[i >> 1] + 1; for(int i = 1; i < 16; i++) buc[1 << i] = i; for(int i = 1; i < n; i++) { int u = read(), v = read(); add(u, v), add(v, u); } dfs(1, 0), cout << f[1] << endl; return 0; }启示:遇到无从下手的问题时先尝试抽象问题并分析性质(本题中是两点间的简单路径必然存在点分树的 LCA)使其更好理解。树上问题先从叶子开始,可以先考虑一些简单情况再尝试总结策略。树形 DP 先考虑仅合并两个子树的情况。
- 1
信息
- ID
- 4930
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者