题目
小Q在电子工艺实习课上学习焊接电路板。一块电路板由若干个元件组成,我们不妨称之为节点,并将其用数字
1,2,3…
1
,
2
,
3
…
进行标号。电路板的各个节点由若干不相交的导线相连接,且对于电路板的任何两个节点,都存在且仅存在一条通路(通路指连接两个元件的导线序列)。在电路板上存在一个特殊的元件称为“激发器”。当激发器工作后,产生一个激励电流,通过导线传向每一个它所连接的节点。而中间节点接收到激励电流后,得到信息,并将该激励电流传向与它连接并且尚未接收到激励电流的节点。最终,激烈电流将到达一些“终止节点”――接收激励电流之后不再转发的节点。激励电流在导线上的传播是需要花费时间的,对于每条边
e
e
,激励电流通过它需要的时间为 ,而节点接收到激励电流后的转发可以认为是在瞬间完成的。现在这块电路板要求每一个“终止节点”同时得到激励电路――即保持时态同步。由于当前的构造并不符合时态同步的要求,故需要通过改变连接线的构造。目前小Q有一个道具,使用一次该道具,可以使得激励电流通过某条连接导线的时间增加一个单位。请问小Q最少使用多少次道具才可使得所有的“终止节点”时态同步?
输入输出格式
输入格式:
第一行包含一个正整数
N
N
,表示电路板中节点的个数。第二行包含一个整数,为该电路板的激发器的编号。接下来
N−1
N
−
1
行,每行三个整数
a,b,t
a
,
b
,
t
。表示该条导线连接节点
a
a
与节点,且激励电流通过这条导线需要
t
t
个单位时间。
输出格式:
仅包含一个整数,为小Q最少使用的道具次数。
输入输出样例
输入样例#1:
3
1
1 2 1
1 3 3
输出样例#1:
2
分析
一道树上DP题,我们正着不好想,所以可以反着想,其实这道题等价于从叶子节点发信号,到达时间均一致,求其所用道具,这样就好做了,我们设
f[i]
f
[
i
]
表示从叶子节点到i节点的时间,们们可轻易想出转移式:
f[i]=max(f[son[i]])
f
[
i
]
=
m
a
x
(
f
[
s
o
n
[
i
]
]
)
,这样就可转移
f[i]
f
[
i
]
了,同时我们记录
f[son[i]]的sum
f
[
s
o
n
[
i
]
]
的
s
u
m
,遍历完子树后,计算
ans+f[i]∗儿子个数−sum
a
n
s
+
f
[
i
]
∗
儿
子
个
数
−
s
u
m
,此时可更新
ans
a
n
s
,最后输出
ans
a
n
s
即可。
上代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll ver[2000010],nxt[2000010],hed[1000010],we[2000010],n,m,tot,dep[1000010],f[1000010],ans=0;
bool vis[1000010];
void add(ll x,ll y,ll z){
ver[++tot]=y,nxt[tot]=hed[x],hed[x]=tot,we[tot]=z;
}
void dfs(ll x){
vis[x]=1;
for(ll i=hed[x],y;i;i=nxt[i]){
if(vis[y=ver[i]])
continue;
vis[y]=1,dep[y]=dep[x]+we[i];
dfs(y);
}
}
void dp(ll x,ll father){
ll mx1=dep[x];
for(ll i=hed[x];i;i=nxt[i]){
if(ver[i]==father)
continue;
dp(ver[i],x);
mx1=max(mx1,f[ver[i]]);
}
f[x]=mx1;
for(ll i=hed[x];i;i=nxt[i]){
if(ver[i]==father)
continue;
ans=ans+f[x]-f[ver[i]];
}
}
int main(){
scanf("%lld%lld",&n,&m);
for(ll i=1,x,y,z;i<n;i++){
scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
memset(vis,0,sizeof(vis));
memset(dep,0,sizeof(dep));
dfs(m);
dp(m,m);
printf("%lld\n",ans);
return 0;
}