Discription
有一棵以1为根的树,他有n个结点,用1到n编号。第i号点有一个值vi。
现在可以对树进行如下操作:
步骤1:在树中选一个连通块,这个连通块必须包含1这个结点。
步骤2:然后对这个连通块中所有结点的值加1或者减1。
问最少要经过几次操作才能把树中所有结点都变成0。
注意:步骤1与步骤2合在一起为一次操作。
Input
单组测试数据。
第一行有一个整数n(1 ≤ n ≤ 10^5)
接下来n-1行,每行给出 ai 和 bi (1 ≤ ai, bi ≤ n; ai ≠ bi),表示ai和bi之间有一条边,输入保证是一棵树。
最后一行有n个以空格分开的整数,表示n个结点的值v1, v2, ..., vn (|vi| ≤ 10^9)。
Output
输出一个整数表示最少的操作步数。
Sample Input
3
1 2
1 3
1 -1 1
Sample Output
3
题解见注释(真的不知道我怎么想到差分约束的2333,网上全是用树上dp做的。。。)
/*
考虑差分约束。
1.设每个点i被add(i)次加操作涉及到,被dec(i)次减操作涉及到, 那么:
add(i) - dec(i) + w[i] = 0.
2.把dec用add表示后,每个点有两个限制:
(1). add(i) >= max{0,-w[i]}
(2). add(i) <= add(fa) + min{0,w[fa]-w[i]}
然后直接跑一个最短路,2*add(1) + w[1] 就是答案.
*/
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int maxn=100005;
vector<int> g[maxn];
int ne[maxn*5],to[maxn*5],num;
int hd[maxn],val[maxn*5],n,m,w[maxn];
bool iq[maxn];
ll d[maxn];
inline void add(int x,int y,int z){
to[++num]=y,ne[num]=hd[x],hd[x]=num,val[num]=z;
}
void dfs(int x,int fa){
add(fa,x,min(0,w[fa]-w[x]));
for(int i=g[x].size()-1,TO;i>=0;i--){
TO=g[x][i];
if(TO==fa) continue;
dfs(TO,x);
}
}
inline void build(){
for(int i=1;i<=n;i++) add(i,0,min(0,w[i]));
dfs(1,1);
}
inline void spfa(){
queue<int> q;
for(int i=0;i<=n;i++) q.push(i),iq[i]=1;
int x;
while(!q.empty()){
x=q.front(),q.pop();
for(int i=hd[x];i;i=ne[i]) if(d[x]+(ll)val[i]<d[to[i]]){
d[to[i]]=d[x]+(ll)val[i];
if(!iq[to[i]]) q.push(to[i]),iq[to[i]]=1;
}
iq[x]=0;
}
for(int i=1;i<=n;i++) d[i]-=d[0];
}
int main(){
scanf("%d",&n);
int uu,vv;
for(int i=1;i<n;i++){
scanf("%d%d",&uu,&vv);
g[uu].pb(vv),g[vv].pb(uu);
}
for(int i=1;i<=n;i++) scanf("%d",w+i);
build();
spfa();
printf("%lld\n",d[1]*2+(ll)w[1]);
return 0;
}