【luogu P6419】Kamp(换根DP)

Kamp

题目链接:luogu P6419

题目大意

一棵树上有一些点有人,边有通过的长度,然后对于每个点,你从这个点出发经过所有人(不用回到原来位置)的最短时间。
其它人不会动,只有你去找人。

思路

首先只考虑一个点,发现如果回到原来位置是比较好搞的,就每次走完子树的里面要的就上来,如果子树里面没有要走的就不走。
(大概是 fx=fy+2ex,y,因为要走过去走回来,注意 y 要保证子树里面有人)

至于多个点换根 DP 即可。

然后考虑不走回来,那你少了的长度就是从最后一个人走回自己的距离。
那贪心的选离你最远的,思考一下发现是可以的因为可以最晚走到的条件是子树内除了自己没有人,安排一下子树的遍历顺序即可。

那这个对于每个点求距离它最远的人其实不难,就分成子树内和字数外,子树内好统计,子树外就维护子树内次小(不能跟最小在同一个儿子里面),然后要么从上面的 up 下来,要么在你这里开始往下(因为不能自己上自己下所以要维护次小)

然后就可以啦!

代码

#include<cstdio> #include<iostream> #define ll long long using namespace std; const int N = 5e5 + 100; int n, k, a[N], le[N], KK; int dis[N][2], sz[N], up[N]; ll ans[N], f[N]; bool p[N]; struct node { int x, to, nxt; }e[N << 1]; void add(int x, int y, int z) {e[++KK] = (node){z, y, le[x]}; le[x] = KK;} ll DP(int now, int father) { ll re = 0; if (p[now]) sz[now] = 1; for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { ll x = DP(e[i].to, now); sz[now] += sz[e[i].to]; if (!x && !p[e[i].to]) continue; x += 2ll * e[i].x; re += x; if (!x) continue; //不能是同一个子树所以不用再比次大 if (dis[now][0] < dis[e[i].to][0] + e[i].x) dis[now][1] = dis[now][0], dis[now][0] = dis[e[i].to][0] + e[i].x; else if (dis[now][1] < dis[e[i].to][0] + e[i].x) dis[now][1] = dis[e[i].to][0] + e[i].x; } return f[now] = re; } void work(int now, int father) { ans[now] = f[now]; for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { if (sz[1] - sz[e[i].to]) { up[e[i].to] = up[now] + e[i].x; if (dis[now][0] == dis[e[i].to][0] + e[i].x) up[e[i].to] = max(up[e[i].to], dis[now][1] + e[i].x); else up[e[i].to] = max(up[e[i].to], dis[now][0] + e[i].x); } bool re = 0, ree = 0; if (f[e[i].to] || p[e[i].to]) { f[now] -= 2ll * e[i].x + f[e[i].to]; re = 1; } if (f[now] || p[now]) { f[e[i].to] += 2ll * e[i].x + f[now]; ree = 1; } work(e[i].to, now); if (ree) { f[e[i].to] -= 2ll * e[i].x + f[now]; } if (re) { f[now] += 2ll * e[i].x + f[e[i].to]; } } } int main() { scanf("%d %d", &n, &k); for (int i = 1; i < n; i++) { int x, y, z; scanf("%d %d %d", &x, &y, &z); add(x, y, z); add(y, x, z); } for (int i = 1; i <= k; i++) scanf("%d", &a[i]), p[a[i]] = 1; ans[1] = DP(1, 0); work(1, 0); for (int i = 1; i <= n; i++) printf("%lld\n", ans[i] - max(up[i], dis[i][0])); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P6419.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示