火山哥周游世界
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