DTOJ #2485. 聚会(kamp)

【题目描述】

一颗树 nn 个点,n1n1 条边,经过每条边都要花费一定的时间,任意两个点都是联通的。

KK 个人(分布在 KK 个不同的点)要集中到一个点举行聚会。

聚会结束后需要一辆车从举行聚会的这点出发,把这 KK 个人分别送回去。

请你回答,对于 i=1ni=1n,如果在第 ii 个点举行聚会,司机最少需要多少时间把 KK 个人都送回家。

【输入格式】

第一行两个数,n,Kn,K

接下来 n1n1 行,每行三个数,xyz 表示 xy 之间有一条需要花费 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

【数据范围与提示】

KN500000

1x,yN,1z1000000

【题解】

看到给出 k 个点,考虑虚树。所以这题并不需要虚树(当然如果你想建没人拦你)

题意是给定一棵树,求从任意点出发走过所有特殊点的最短路径。

先考虑回到出发点的完整回路。由于树上两点间路径只有一条,而每个特殊点都必须到达,所以所有特殊点到出发的路径都至少走了两遍。而显然每条边最多只会走两遍,因此计算所有特殊点到出发点路径的边权和的两倍即可。

考虑不回到出发点。显然可以贪心,找出最远的特殊点,将答案减掉该路径长路一倍即可。

至于换根,是经典的换根 dpdfs 两遍即可维护。注意第二次 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]);
}

  

posted @   DTOI_RSY  阅读(237)  评论(0编辑  收藏  举报
编辑推荐:
· 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编程运行原理
点击右上角即可分享
微信分享提示