DTOJ #2485. 聚会(kamp)
【题目描述】
一颗树 nn 个点,n−1n−1 条边,经过每条边都要花费一定的时间,任意两个点都是联通的。
有 KK 个人(分布在 KK 个不同的点)要集中到一个点举行聚会。
聚会结束后需要一辆车从举行聚会的这点出发,把这 KK 个人分别送回去。
请你回答,对于 i=1⋯ni=1⋯n,如果在第 ii 个点举行聚会,司机最少需要多少时间把 KK 个人都送回家。
【输入格式】
第一行两个数,n,Kn,K。
接下来 n−1n−1 行,每行三个数,x,y,z 表示 x 到 y 之间有一条需要花费 z 时间的边。
接下来 K 行,每行一个数,表示 K 个人的分布。
【输出格式】
输出 n 个数,第 i 行的数表示:如果在第 i 个点举行聚会,司机需要的最少时间。
【样例】
样例输入
7 2
1 2 4
1 3 1
2 5 1
2 4 2
4 7 3
4 6 2
3
7
样例输出
11
15
10
13
16
15
10
【数据范围与提示】
K≤N≤500000
1≤x,y≤N,1≤z≤1000000
【题解】
看到给出 k 个点,考虑虚树。所以这题并不需要虚树(当然如果你想建没人拦你)。
题意是给定一棵树,求从任意点出发走过所有特殊点的最短路径。
先考虑回到出发点的完整回路。由于树上两点间路径只有一条,而每个特殊点都必须到达,所以所有特殊点到出发的路径都至少走了两遍。而显然每条边最多只会走两遍,因此计算所有特殊点到出发点路径的边权和的两倍即可。
考虑不回到出发点。显然可以贪心,找出最远的特殊点,将答案减掉该路径长路一倍即可。
至于换根,是经典的换根 dp,dfs 两遍即可维护。注意第二次 dfs 从上往下更新 dp 值时要记录最长链和次长链,保证不会重复。
【代码】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #include<bits/stdc++.h> inline int read ( void ) { int x=0; char ch; bool f= true ; while ( ! isdigit (ch= getchar ()) ) if ( ch== '-' ) f= false ; for ( x=ch^48; isdigit (ch= getchar ()); ) x=(x<<1)+(x<<3)+(ch^48); return f ? x : -x ; } const int maxn=500000+10; int n,k,h[maxn],cnt; bool flag[maxn]; long long ds[maxn],us[maxn],max[maxn],max1[maxn],max2[maxn],ans[maxn]; struct edge { int v,w,nxt; } e[maxn<<1]; inline void dfs ( int u, int fr ) { for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].v!=fr ) { dfs(e[i].v,u); if ( flag[e[i].v] or ds[e[i].v] ) { ds[u]+=e[i].w+ds[e[i].v]; if ( max1[e[i].v]+e[i].w>max1[u] ) max2[u]=max1[u],max1[u]=max1[e[i].v]+e[i].w; else if ( max1[e[i].v]+e[i].w>max2[u] ) max2[u]=max1[e[i].v]+e[i].w; } } } inline void DFS ( int u, int fr ) { ans[u]=2*(ds[u]+us[u])-std::max(max1[u],max[u]); for ( int i=h[u];i;i=e[i].nxt ) if ( e[i].v!=fr ) { if ( flag[e[i].v] or ds[e[i].v] ) ds[u]-=ds[e[i].v]+e[i].w; if ( ds[u] or us[u] ) us[e[i].v]=ds[u]+us[u]+e[i].w; if ( max[u] ) max[e[i].v]=max[u]+e[i].w; if ( max1[u] ) { if ( max1[e[i].v]+e[i].w<max1[u] ) max[e[i].v]=std::max(max[e[i].v],max1[u]+e[i].w); else if ( max2[u] ) max[e[i].v]=std::max(max[e[i].v],max2[u]+e[i].w); } DFS(e[i].v,u); if ( flag[e[i].v] or ds[e[i].v] ) ds[u]+=ds[e[i].v]+e[i].w; } } signed main() { n=read();k=read(); for ( int i=1,u,v,w;i<n;i++ ) u=read(),v=read(),w=read(),e[++cnt].nxt=h[u],e[h[u]=cnt].v=v,e[cnt].w=w,e[++cnt].nxt=h[v],e[h[v]=cnt].v=u,e[cnt].w=w; for ( int i=1;i<=k;i++ ) flag[read()]= true ; dfs(1,0);DFS(1,0); for ( int i=1;i<=n;i++ ) printf ( "%lld\n" ,ans[i]); } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理