P6419 [COCI2014-2015#1] Kamp(换根树形DP)

· · 题解

题意

显然是换根,先考虑以1为根进行第一次dfs:
设f_x表示从x出发到子树中每个标记点后返回x的最小距离,dis_{0,x}表示x到子树中的标记点的最远距离。

显然答案为f_1-dis_{0,1}。

考虑换根:
我们为了换根,需要再维护dis_{1,x}表示x到子树中的标记点的次远距离,且保证最远和次远不在同一棵子树中。

我们考虑x\not =1的答案包含x的子树与x的子树外,因此设g_x表示x子树外的答案,up_x表示x到子树外标记点的最远距离。

转移方程都好写,可以看代码。 code: ``` #include<bits/stdc++.h> using namespace std; #define int long long const int maxn=5e5+10; int n,m,cnt_edge; int head[maxn],a[maxn],size[maxn],f[maxn],g[maxn],up[maxn]; int dis[2][maxn]; struct edge{int to,nxt,dis;}e[maxn<<1]; inline int read() { char c=getchar();int res=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar(); return res*f; } inline void add_edge(int u,int v,int w) { e[++cnt_edge].nxt=head[u]; head[u]=cnt_edge; e[cnt_edge].to=v; e[cnt_edge].dis=w; } void dfs1(int x,int fa) { size[x]=a[x]; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa)continue; dfs1(y,x);size[x]+=size[y]; if(size[y]) { f[x]+=f[y]+e[i].dis*2; if(dis[0][y]+e[i].dis>dis[0][x])dis[1][x]=dis[0][x],dis[0][x]=dis[0][y]+e[i].dis; else if(dis[0][y]+e[i].dis>dis[1][x])dis[1][x]=dis[0][y]+e[i].dis; } } } void dfs2(int x,int fa) { for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa)continue; if(m-size[y]) { g[y]=g[x]+f[x]-f[y]; if(!size[y])g[y]+=2*e[i].dis; if(dis[0][x]==dis[0][y]+e[i].dis)up[y]=max(up[x],dis[1][x])+e[i].dis; else up[y]=max(up[x],dis[0][x])+e[i].dis; } dfs2(y,x); } } signed main() { n=read(),m=read(); for(int i=1;i<n;i++) { int u=read(),v=read(),w=read(); add_edge(u,v,w),add_edge(v,u,w); } for(int i=1;i<=m;i++)a[read()]++; dfs1(1,0);dfs2(1,0); for(int i=1;i<=n;i++)printf("%lld\n",f[i]+g[i]-max(dis[0][i],up[i])); return 0; } ```