火山哥周游世界

https://ac.nowcoder.com/acm/contest/4114/G

题意:

一颗n个点的无根带权树,有k个特殊点,可以走到任一个点停止,现在要让你求对于每个点i作为起始点,走遍所有特殊点所花的最小路径和,n<=5e5 n<=5e5n<=5e5

思路:

一颗n个点的无根带权树,有k个特殊点,可以走到任一个点停止,现在要让你求对于每个点i作为起始点,走遍所有特殊点所花的最小路径和,n<=5e5 n<=5e5n<=5e5

先考虑若要从起点出发,最后回到起点所需要的路径和,这个是到所有特殊点的所有边权加起来乘2,去和回。

若不回到起点选择某个特殊点作为终点停止,则是上述路径和减去从起点到该特殊点的路径和,故要让结果最小,必然是减去一个距离起点最远的特殊点的路径,用sum表示经过的所有边权和,len[x]表示从起点到特殊点的距离,那么在确定了起点的情况下可以得到一个这样的式子:sum∗2−max(x∈[特殊点],len[x]) sum*2-max(x \in [特殊点] ,len[x])sum∗2−max(x∈[特殊点],len[x])。

也就是说只要算出来每个点作为根时的sum与那个max则可以解决该题了。

于是考虑二次扫描,第一次先算出1作为根时的答案,一次dfs即可算出。

第二次dfs时算每个点的sum与max。

先看sum,对于一个点x,有两部分组成,一个是x向下延展,另一个是通过父亲节点走向其他特殊点,此时只需要判断是否需要向下(下方有特殊点),以及是否需要向上即可。
再看max,max也是来自两部分,向下的和通过父亲节点出去的,这里需要在第一次dfs时维护路径最大值与次大值 ,这一部分有关树的中心的知识。

链接:https://www.cnblogs.com/pangbi/p/12452099.html(树的中心)

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long ll;
 5 
 6 const int maxn=5e5+7;
 7 struct Edge{
 8     int v,w,next;
 9 }edge[maxn<<1];
10 
11 int head[maxn],top;
12 void init(){
13     top=0;
14     memset(head,-1,sizeof(head));
15 }
16 
17 void add(int u,int v,int w){
18     edge[top].v=v;
19     edge[top].w=w;
20     edge[top].next=head[u];
21     head[u]=top++;
22 }
23 
24 ll siz[maxn]; //以1为根,x的子树中国家数量;
25 ll sum[maxn]; //以1为根,x的子树中到国家并返回的路径和;
26 ll res[maxn]; //以x为根,到所有国家并返回的路径和;
27 
28 ll Fmax[maxn]; //以1为根,到子树的最大值;
29 ll Smax[maxn]; //以1为根,到子树的次大值;
30 ll neber[maxn]; //以1为根时,最大值那个节点;
31 
32 ll Up[maxn]; //以1为根,x通过父亲走到国家的最大值;
33 
34 bool f[maxn]; // 国家;
35 
36 int n,k;
37 void dfs(int u,int fa){
38     if(f[u]) siz[u]=1;
39     for(int i=head[u];i!=-1;i=edge[i].next){
40         int v=edge[i].v,w=edge[i].w;
41         if(v==fa) continue;
42         dfs(v,u);
43         siz[u]+=siz[v];
44         sum[u]+=sum[v];
45         if(siz[v]){
46             sum[u]+=2*w;
47             if(Fmax[v]+w>=Fmax[u]){
48                 Smax[u]=Fmax[u];
49                 Fmax[u]=Fmax[v]+w;
50                 neber[u]=v;
51             }
52             else{
53                 Smax[u]=max(Smax[u],Fmax[v]+w);
54             }
55         }
56     }
57 }
58 
59 ll ans[maxn];
60 
61 void dfs2(int u,int fa){
62     for(int i=head[u];i!=-1;i=edge[i].next){
63         int v=edge[i].v,w=edge[i].w;
64         if(v==fa) continue;
65         if(k-siz[v]){
66             if(neber[u]==v) Up[v]=max(Up[u],Smax[u])+w;
67             else Up[v]=max(Up[u],Fmax[u])+w;
68         }
69         res[v]=res[u]-sum[v];
70         if(siz[v]) res[v]-=w*2;
71         res[v]+=sum[v];
72         if(k-siz[v]) res[v]+=2*w;
73         //res[v]=res[u]-sum[v]-siz[v]*w+(k-siz[v])*w+sum[v];
74         dfs2(v,u);
75     }
76 }
77 
78 int main(){
79     int u,v,w;
80     scanf("%d%d",&n,&k);
81     init();
82     for(int i=1;i<n;++i){
83         scanf("%d%d%d",&u,&v,&w);
84         add(u,v,w),add(v,u,w);
85     }
86     for(int i=1;i<=k;++i) scanf("%d",&u),f[u]=1;
87     dfs(1,0);
88     //for(int i=1;i<=n;++i) cout<<Fmax[i]<<" "<<Smax[i]<<" "<<neber[i]<<" "<<sum[i]<<endl;
89     res[1]=sum[1];
90     dfs2(1,0);
91     //for(int i=1;i<=n;++i) cout<<res[i]<<endl;
92     for(int i=1;i<=n;++i) ans[i]=res[i]-max(Up[i],Fmax[i]);
93     for(int i=1;i<=n;++i) printf("%lld\n",ans[i]);
94     return 0;
95 }

 


————————————————
版权声明:本文为CSDN博主「cy41」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chenyume/article/details/104098093

posted @ 2020-03-09 22:26  古比  阅读(296)  评论(1编辑  收藏  举报