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;
}