题目
Normalgod被扔进了一间实验室。这间实验室是一棵有n个节点的树。现在Normalgod在一号节点,出口也在一号节点,但为了打开它,必须经过每一个节点按下每个节点的开关,出口才能打开。GLaDOS为了杀死Normalgod,开始在实验室里释放毒气,因此Normalgod必须尽快逃出这间实验室。
当然,Normalgod可以使用传送枪。传送枪可以发射出两个颜色不同的传送门。Normalgod可以从其中一个传送到另一个。尽管传送枪可以在视野范围内的任何一个经过特殊处理的表面打开一扇传送门,但这间实验室的设计使得Normalgod只能在他所处的房间内打开一个传送门。 在已经存在了一个同颜色的传送门时,打开新的传送门会使与它同颜色的旧门消失。传送和打开传送门所需时间为0。
显然,利用传送枪会让Normalgod更快解决谜题,可Normalgod死在了按下最后一个按钮的路上。尽管如此,GLaDOS还是很想知道到底Normalgod最快能用多久逃出去,这对她的实验室设计方法论有重要的指导作用。作为GLaDOS的算法模块,你要完成这个任务。
题解
一条边最少走1次,最多走2次。
如何求出走一次的边?
设f[x]表示以x为根的子树的答案。
y是x的儿子,那么y对f[x]的贡献为:两种情况的最小值。
情况一,(x,y)走2次。f[y]+2*weight(x,y)
情况二,(x,y)走1次。显然通过传送门跳回到x时,y的子树已遍历完。
这种情况下,显然离y最远的,y的子树的点到y的这条链只走一次,y的子树的其他边走2次。
将x的所有儿子对f[x]的贡献加起来即可。
代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 1000010
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{
int to,next,val;
}edge[N<<1];
int tot,head[N];
int i,j,k,l,n,m;
int fa[N],son[N],g[N];
int u,v,w;
int st[N];
LL ans;
int read(){
int rs=0,fh=1;char ch;
while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
if(ch=='-')fh=-1,ch=getchar();
while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
return fh*rs;
}
void lb(int x,int y,int z){
edge[++tot].to=y;
edge[tot].next=head[x];
edge[tot].val=z;
head[x]=tot;
}
void dg(int x){
int i;
for(i=head[x];i;i=edge[i].next)
if(edge[i].to^fa[x]){
fa[edge[i].to]=x;
son[x]++;
g[edge[i].to]=edge[i].val;
dg(edge[i].to);
}
}
int main(){
n=read();
fo(i,1,n-1){
u=read();v=read();w=read();
lb(u,v,w);
lb(v,u,w);
ans=ans+1ll*2*w;
}
dg(1);
fo(i,1,n)if(!son[i]){
u=i;
while(u^1){
ans=ans-1ll*g[u];
u=fa[u];
if(son[u]>1)break;
}
}
printf("%lld",ans);
return 0;
}