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

小粉兔
Always continue; Never break;搬运于
2025-08-24 21:47:41,当前版本为作者最后更新于2019-02-08 23:04:45,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
在博客园食用更佳:https://www.cnblogs.com/PinkRabbit/p/10356940.html
题意简述:
一棵 个节点的树,边有边权。
每个点可能是关键点,每次操作改变一个点是否是关键点。
求所有关键点形成的极小连通子树的边权和的两倍。
题解:
有一个结论:DFS 序求出后,假设关键点按照 DFS 序排序后是 。
那么所有关键点形成的极小连通子树的边权和的两倍等于 $\mathrm{dist}(a_1,a_2)+\mathrm{dist}(a_2,a_3)+\cdots+\mathrm{dist}(a_{k-1},a_k)+\mathrm{dist}(a_k,a_1)$。
画个图感性理解一下,应该是很好懂的。
那么求一下 DFS 序,每次操作相当于往集合里加入/删除一个元素。
假设插入 ,它DFS序左右两边分别是 和 。那么答案加上 $\mathrm{dist}(x,y)+\mathrm{dist}(x,z)-\mathrm{dist}(y,z)$ 即可。
删除同理。还有,求 LCA 就用个倍增或者树剖吧,Tarjan 离线比较麻烦。
用 STL 自带的 set 容器维护起来很方便。你也可以手写树状数组/线段树/平衡树。
#include <cstdio> #include <set> typedef long long LL; const int MN = 100005; int N, M; int h[MN], nxt[MN * 2], to[MN * 2], w[MN * 2], tot; inline void ins(int x, int y, int z) { nxt[++tot] = h[x], to[tot] = y, w[tot] = z, h[x] = tot; } int dfn[MN], idf[MN], dfc; int dep[MN], faz[MN][17]; LL dis[MN]; void DFS(int u, int fz) { dfn[u] = ++dfc; idf[dfc] = u; dep[u] = dep[faz[u][0] = fz] + 1; for (int j = 1; 1 << j < dep[u]; ++j) faz[u][j] = faz[faz[u][j - 1]][j - 1]; for (int i = h[u]; i; i = nxt[i]) if (to[i] != fz) dis[to[i]] = dis[u] + w[i], DFS(to[i], u); } inline int lca(int x, int y) { if (dep[x] < dep[y]) std::swap(x, y); for (int d = dep[x] - dep[y], j = 0; d; d >>= 1, ++j) if (d & 1) x = faz[x][j]; if (x == y) return x; for (int j = 16; ~j; --j) if (faz[x][j] != faz[y][j]) x = faz[x][j], y = faz[y][j]; return faz[x][0]; } inline LL dist(int x, int y) { return dis[x] + dis[y] - 2 * dis[lca(x, y)]; } bool vis[MN]; std::set<int> st; std::set<int>::iterator it; LL Ans; int main() { scanf("%d%d", &N, &M); for (int i = 1, x, y, z; i < N; ++i) { scanf("%d%d%d", &x, &y, &z); ins(x, y, z), ins(y, x, z); } DFS(1, 0); for (int m = 1, x, y, z; m <= M; ++m) { scanf("%d", &x); x = dfn[x]; if (!vis[idf[x]]) st.insert(x); y = idf[(it = st.lower_bound(x)) == st.begin() ? *--st.end() : *--it]; z = idf[(it = st.upper_bound(x)) == st.end() ? *st.begin() : *it]; if (vis[idf[x]]) st.erase(x); x = idf[x]; LL d = dist(x, y) + dist(x, z) - dist(y, z); if (!vis[x]) vis[x] = 1, Ans += d; else vis[x] = 0, Ans -= d; printf("%lld\n", Ans); } return 0; }
- 1
信息
- ID
- 2393
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者