• DTOJ #2485. 聚会(kamp)


    【题目描述】

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

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

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

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

    【输入格式】

    第一行两个数,$n,K$。

    接下来 $n-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 le N le 500000 $

    $ 1 le x,y le N, 1 le z le 1000000 $

    【题解】

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

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

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

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

    至于换根,是经典的换根 $dp$,$dfs$ 两遍即可维护。注意第二次 $dfs$ 从上往下更新 $dp$ 值时要记录最长链和次长链,保证不会重复。

    【代码】

    #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
    ",ans[i]);
    }
    

      

  • 相关阅读:
    网页布局1
    下拉菜单的制作
    状态玻璃效果菜单(实例)
    鼠标经过时整行变色
    鼠标经过时单元格变色
    Bootstrap3.0入门学习系列规划[持续更新]
    使用jQuery实现简单的拖动效果
    解决VS2012新建MVC3等项目时,收到加载程序集“NuGet.VisualStudio.Interop…”的错误
    Flash3D引擎:Away3D 4.1 Alpha版介绍
    Away3d 基础 1 ---对一个简单类的解释
  • 原文地址:https://www.cnblogs.com/RenSheYu/p/11318300.html
Copyright © 2020-2023  润新知