B - Zero Tree
第一件最重要的事情,看清楚题目大意,
我先说说我看错的题意,给你一棵n个节点的树,每个节点都有一个值,你可以进行一种操作:
- 选择一个包含1的子树(这个子树的概念我也弄错了,我以为这个包含1的子树必须延申到叶子节点)
- 对这棵子树的每一个节点的值+1,或者-1
问最少的操作使得整棵树的值之和为0。
//////////////////////
完全不是这个题意(((φ(◎ロ◎;)φ)))
正确题意:
给你一棵n个节点的树,每个节点都有一个值,你可以进行一种操作:
- 选择一个包含1的子树,这个子树就是选择任意几个节点相互连接。
- 对这棵子树的每一个节点+1,或者-1
问最少的操作让这棵树上的每一个节点的值等于0。
题解:
第一种题意,我觉得还是稍微难一点,猜想把所有满足条件的子树的大小求出来,然后背包?
现在来考虑第二种题意,因为必须要节点1,所以把1当作根节点,这样对于叶子节点如果一个叶子节点 (x) ,(a[x]=y) 那么必须操作 (y) 次,所以把所有叶子节点的操作数求出来向上传递,每一个节点他的操作数如何计算呢?
我们定义 (dp1[u]) 表示这个 (u) 节点进行 (+) 操作的数量
定义 (dp2[u]) 表示这个节点 (u) 进行 (-) 操作的数量
所以 (dp1[u]=max(dp1[v])) (dp2[u]=max(dp2[v]))
最后再加上操作完之后本身要进行操作的数量。
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
#define debug(x) printf("debug:%s=%d
",#x,x);
//#define debug(x) cout << #x << ": " << x << endl
using namespace std;
const int maxn = 1e5+10;
typedef long long ll;
int head[maxn<<1],nxt[maxn<<1],to[maxn<<1],cnt;
void add(int u,int v){
++cnt,to[cnt]=v,nxt[cnt]=head[u],head[u]=cnt;
++cnt,to[cnt]=u,nxt[cnt]=head[v],head[v]=cnt;
}
ll a[maxn],dp1[maxn],dp2[maxn];
void dfs(int u,int pre){
dp1[u]=dp2[u]=0;
for(int i=head[u];i;i=nxt[i]){
int v = to[i];
if(v==pre) continue;
dfs(v,u);
dp1[u]=max(dp1[u],dp1[v]);
dp2[u]=max(dp2[u],dp2[v]);
}
a[u]+=dp1[u]-dp2[u];
if(a[u]>0) dp2[u]+=a[u];
else dp1[u]+=abs(a[u]);
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
dfs(1,0);
printf("%lld
",dp1[1]+dp2[1]);
return 0;
}