题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3743
这道题第一想法是暴力。
但是没有细分数据点。
硬想了半天没有思路。
只好去搜题解。但是题解看着好麻烦的。
于是就综合了很多题解的思路。
选择用两次树形DP
第一次先求一些必要的值。
把第一个关键点设为根。从它开始DP。
第二次也是一样,以第一个为根,更新答案。
细节可以看注释。
如下。
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=500005; int n,k,tot,head[maxn],d[maxn]; bool have[maxn<<1]; struct node{ int nxt,dis,to; #define nxt(x) e[x].nxt #define dis(x) e[x].dis #define to(x) e[x].to }e[maxn<<1]; inline void add(int from,int to,int v){ to(++tot)=to;dis(tot)=v; nxt(tot)=head[from];head[from]=tot; } int num[maxn];//以它为根的子树中有多少关键点。 ll f[2][maxn],val[maxn];//f[0][x]表示从x向下走到下一个关键点的最大长,f[1][x]是次大 inline void doo1(int now,int fa){ num[now]=have[now]; for(int i=head[now];i;i=nxt(i)){ int t=to(i); if(t==fa) continue; doo1(t,now); if(!num[t]) continue; num[now]+=num[t]; val[now]+=val[t]+dis(i); int tmp=f[0][t]+dis(i); if(tmp>f[0][now]){ f[1][now]=f[0][now]; f[0][now]=tmp; }else if(tmp>f[1][now]) f[1][now]=tmp; } } ll ans[maxn];//答案 inline void push(int now,int fa){ for(int i=head[now];i;i=nxt(i)){ int t=to(i); if(t==fa) continue; ans[t]=ans[now]+dis(i); push(t,now); } } //up表示now到上一个关键点的距离 inline void doo2(int now,int fa,ll up,ll sum){ ans[now]=2*sum-max(up,f[0][now]);//更新答案,画图可以得到 for(int i=head[now];i;i=nxt(i)){ int t=to(i); if(t==fa) continue; if(num[t]){//如果to是关键点求up ll tmp=0; if(up||have[now]) tmp=max(tmp,up+dis(i)); if(f[0][now]==f[0][t]+dis(i)){ if(f[1][now]) tmp=max(tmp,f[1][now]+dis(i)); }else tmp=max(tmp,f[0][now]+dis(i)); doo2(t,now,tmp,sum); }else{//直接求然后下放 ans[t]=ans[now]+dis(i); push(t,now); } } } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n-1;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,z); } for(int i=1;i<=k;i++) scanf("%d",&d[i]),have[d[i]]=1; doo1(d[1],0); doo2(d[1],0,0,val[d[1]]); for(int i=1;i<=n;i++) printf("%lld ",ans[i]); system("pause"); return 0; }