#4703. 旅行
题解
考虑暴力以每个点为根,如果要回到这个点的话那就是到k个点的不重路径的总和的两倍,所以再减去最远点的路径长即可,dp即可
考虑正解,发现可以换根,于是 $O(n)$ 即可
代码
#include <bits/stdc++.h> #define LL long long using namespace std; const int N=5e5+5,M=N<<1; int n,k,t,sz[N],fl[N],hd[N],V[M],W[M],nx[M]; LL f[N],g[N],ans[N]; void add(int u,int v,int w){ nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w; } void dfs(int x,int fr){ sz[x]=fl[x];f[x]=g[x]=0; for (int i=hd[x];i;i=nx[i]) if (V[i]!=fr){ dfs(V[i],x);sz[x]+=sz[V[i]]; if (sz[V[i]]) f[x]+=f[V[i]]+2ll*W[i], g[x]=max(g[x],g[V[i]]+W[i]); } } void dp(int x,int fr,int w){ ans[x]=f[x]-g[x]; LL ax1=0,ax2=0;int id=0; for (int i=hd[x];i;i=nx[i]) if (V[i]!=fr && sz[V[i]]){ if (ax1<g[V[i]]+W[i]) ax2=ax1,ax1=g[V[i]]+W[i],id=V[i]; else ax2=max(ax2,g[V[i]]+W[i]); } if (sz[fr]){ if (ax1<g[fr]+w) ax2=ax1,ax1=g[fr]+w,id=fr; else ax2=max(ax2,g[fr]+w); } for (int i=hd[x];i;i=nx[i]) if (V[i]!=fr){ if (sz[V[i]]){ f[x]-=f[V[i]]+2ll*W[i]; if (id==V[i]) g[x]=ax2; f[V[i]]+=f[x]+2ll*W[i]; g[V[i]]=max(g[V[i]],g[x]+W[i]); } else f[V[i]]=f[x]+2ll*W[i],g[V[i]]=g[x]+W[i]; sz[x]-=sz[V[i]];sz[V[i]]+=sz[x];dp(V[i],x,W[i]); sz[V[i]]-=sz[x];sz[x]+=sz[V[i]]; if (sz[V[i]]){ f[V[i]]-=f[x]+2ll*W[i]; f[x]+=f[V[i]]+2ll*W[i];g[x]=ax1; } } } int main(){ scanf("%d%d",&n,&k); for (int x,y,z,i=1;i<n;i++) scanf("%d%d%d",&x,&y,&z), add(x,y,z),add(y,x,z); for (int x,i=1;i<=k;i++) scanf("%d",&x),fl[x]=1; dfs(1,0),dp(1,0,0); for (int i=1;i<=n;i++) printf("%lld\n",ans[i]); return 0; }