1729 旅行 (树形dp 二次扫描与换根)
原创 罗方炜 信息学luoOJ 5天前
小C上周末和他可爱的同学小A一起去X湖玩。
X湖景区一共有n个景点,这些景点由n-1条观光道连接着,从每个景点开始都可以通过观光道直接或间接地走到其他所有的景点。小C带着小A从1号景点开始游玩。游览完第一个景点后,先由小C决定下一个游览的景点,他们一起走去那个景点玩。接下来,他们轮流决定他们下一步去哪个景点玩。他们不会选择已经走过的景点,因为重复游览一个景点是无趣的。当他们无法选择下一个景点时,他们就结束旅程。
小C是好动的男孩纸,所以他希望游览的过程尽量长,也就是走过观光道的长度和最大。而小A是文静的女孩纸,她希望游览的过程尽量短。小A和小C都极度聪明,且他们的目光都足够长远,他们做出的决策都是对自己最优的。由于小C在旅游前就仔细研究了X湖景区的地图,他可以在旅行开始前就用自己惊人的数学能力推算出他和小A旅行的路径长度。
小C的梦境是美好的。在他的梦里,他和小A又进行了n-1次旅行,第i次旅行从i+1号点开始,每次也是小C先决定下一个景点,然后小A,然后小C……直到旅行结束。现在小C希望你对于所有n次旅行,求出他和小A旅行的路径长度。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=300005;
int n,tot,head[M],Next[M*2],vet[M*2],len[M*2];
ll f1[M],f2[M],g1[M],g2[M],ma[M],mi[M],ans[M];
//f1[i]表示i向下走最大值,f2[i]次大值,g1[i]最小值,g2[i]次小值
//ma[i]表示二次扫描时,i向上方向的最大值,mi[i]最小值
void add(int a,int b,int c){
Next[++tot]=head[a], vet[tot]=b, len[tot]=c;
head[a]=tot;
}
void dfs(int x,int pre){
f1[x]=f2[x]=0, g1[x]=g2[x]=1e16;
int c=0;
for(int i=head[x]; i; i=Next[i]){
int y=vet[i]; if(y==pre) continue;
c++;
dfs(y,x);
ll maxv=g1[y]+len[i],minv=f1[y]+len[i]; //临时变量也得开ll
if(maxv>f1[x]){ //维护最大次大
f2[x]=f1[x];
f1[x]=maxv;
}else if(maxv>f2[x]) f2[x]=maxv;
if(minv<g1[x]){
g2[x]=g1[x];
g1[x]=minv;
}else if(minv<g2[x]) g2[x]=minv;
}
if(c==0) g1[x]=g2[x]=0; //x为叶子节点
}
void dfs2(int x,int pre){
for(int i=head[x]; i; i=Next[i]){
int y=vet[i], z=len[i];
if(y==pre) continue;
if(f1[x]==g1[y]+z)
mi[y]=max(ma[x],f2[x])+z;
else
mi[y]=max(ma[x],f1[x])+z;
if(g1[x]==f1[y]+z)
ma[y]=min(mi[x],g2[x])+z;
else
ma[y]=min(mi[x],g1[x])+z;
ans[y]=max(f1[y],ma[y]);
dfs2(y,x);
}
}
int main(){
scanf("%d",&n);
for(int i=2; i<=n; i++){
int a,b,c; scanf("%d%d%d",&a,&b,&c);
add(a,b,c); add(b,a,c);
}
dfs(1,0);
ans[1]=f1[1]; mi[1]=1e16;
if(g2[1]==1e16) g2[1]=0;
dfs2(1,0);
for(int i=1; i<=n; i++) printf("%lld\n",ans[i]);
return 0;
}