http://codeforces.com/problemset/problem/274/B
题目大意:
给定你一颗树,每个点上有权值。
现在你每次取出这颗树的一颗子树(即点集和边集均是原图的子集的连通图)且这颗树中必须包含节点1
然后将这颗子树中的所有点的点权+1或-1
求把所有点权全部变为0的最小次数(n<=10^5)
题解:
因为每一次的子树中都必须有1,所以我们得知每一次变换的话1的权值都会变化
所以我们以1为根
现在,我们发现,如果一个节点的权值发生变化,那么他的父节点的权值一定发生变化
而一个点因为其子节点而导致的,不是用于平衡自己权值的变化是不可避免的
所以我们考虑最小化这个值,我们假设现在他的所有儿子都是叶子节点
那么节点u被变化的值即为inc[u] + dec[u]其中inc[u] = max{inc[v]},dec[u] = max{dec[v]}
inc[u]表示这个节点在最优情况下被加了多少次
dec[u]表示这个节点在最坏情况下被减了多少次
所以我们知道,在我们处理完了u的所有子树的问题后,就要解决自己本身的问题
所以这是根据val[u]的正负来调整inc,和dec
O(n)dfs即可
code
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef long long ll; 6 template<typename T>inline void read(T &x){ 7 x=0;char ch;bool flag = false; 8 while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true; 9 while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x; 10 } 11 template<typename T>inline T cat_max(const T &a,const T &b){return a>b ? a:b;} 12 template<typename T>inline T cat_min(const T &a,const T &b){return a<b ? a:b;} 13 const int maxn = 100010; 14 struct Node{ 15 int to,next; 16 }G[maxn<<1]; 17 int head[maxn],cnt; 18 void add(int u,int v){ 19 G[++cnt].to = v; 20 G[cnt].next = head[u]; 21 head[u] = cnt; 22 } 23 ll val[maxn],inc[maxn],dec[maxn]; 24 int fa[maxn]; 25 #define v G[i].to 26 void dfs(int u){ 27 for(int i = head[u];i;i=G[i].next){ 28 if(v == fa[u]) continue; 29 fa[v] = u; 30 dfs(v); 31 inc[u] = cat_max(inc[u],inc[v]); 32 dec[u] = cat_max(dec[u],dec[v]); 33 } 34 val[u] = val[u] + inc[u] - dec[u]; 35 if(val[u] <= 0) inc[u] += -val[u]; 36 else dec[u] += val[u]; 37 return; 38 } 39 #undef v 40 int main(){ 41 int n;read(n); 42 for(int i=1,u,v;i<n;++i){ 43 read(u);read(v); 44 add(u,v);add(v,u); 45 }for(int i=1;i<=n;++i) read(val[i]); 46 dfs(1); 47 printf("%lld\n",inc[1]+dec[1]); 48 getchar();getchar(); 49 return 0; 50 } 51