题意翻译
题目描述
一棵树是一个有n个节点与正好n-1条边的图;并且符合以下条件:对于任意两个节点之间有且只有一条简单路径。
我们定义树T的子树为一棵所有节点是树T节点的子集,所有边是T边的子集的树。
给定一颗有n个节点的树,假设它的节点被编号为1到n。每个节点有一个权值,v_ivi表示编号为i的节点的权值。你需要进行一些操作,每次操作符合以下规定:
- 在给定的这棵树中选择一棵子树,并保证子树中含有节点1
- 把这棵子树中的所有节点加上或减去1
你需要计算至少需要多少次操作来让所有的节点的权值归零。 输入数据
第一行包含一个整数n,表示树中节点的数量
接下来的n-1行,一行两个整数u,v,表示u和v之间有一条边(u!=v)。
最后一行包含n个整数v_ivi,用空格隔开,表示每个节点的权值 输出数据
一行一个整数,输出最小需要的操作次数。 输入样例
3
1 2
1 3
1 -1 1
输出样例
3
数据规模 对于30\%30%的数据,n\leq100,|vi|\leq1000n≤100,∣vi∣≤1000
对于50\%50%的数据,n\leq10^4n≤104
对于100\%100%的数据,n\leq10^5,|vi|\leq10^9n≤105,∣vi∣≤109
Translated by 首相大大
输入输出样例
输入 #1复制
3 1 2 1 3 1 -1 1
输出 #1复制
3
题意:每次选的时候一定要包含1,但是子树的大小不定,具体意思就是说选择了一个点,从这个点往上直到根节点1的经过的点都要同时加1/-1;
考虑树形dp,add[x]:x点增加的次数,del[x]:x点减少的次数。
对于父节点,遍历其儿子得出add[x]=max(add[x],add[v]),del[x]=max(del[x],del[v]);
原因在于,比如叶子节点a是7,叶子节点b是5,那么更新的时候,开始叶子节点a和叶子节点b同时更新-1,到了叶子节点b为0了,就单单更新叶子节点a。这样就是说其a和b的父亲节点更新最大的7就可以。那么-7和-5同理。
当叶子节点a是7,b是-5,那么两个点开始同时更新+1或者-1肯定不是最小,对b以及路上的+5,对a以及路上的-7,这样其a和b的父亲节点最终的更新值a[x]+=(add[x]-del[x]),也就是a[x]-=2,然后此时a和b叶子节点都为1,此时父亲节点重复同样的事情。树形dp即可。
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e5+100;
typedef long long LL;
LL a[maxn],add[maxn],del[maxn],ans=0;
vector<LL>g[maxn];
void dfs(LL u,LL fa)
{
for(LL i=0;i<g[u].size();i++){
LL v=g[u][i];
if(v==fa) continue;
dfs(v,u);
add[u]=max(add[u],add[v]);
del[u]=max(del[u],del[v]);
}
a[u]+=add[u]-del[u];
if(a[u]>0){
del[u]+=a[u];
}
else{
add[u]+=(-a[u]);
}
}
int main(void)
{
cin.tie(0);std::ios::sync_with_stdio(false);
LL n;cin>>n;
for(LL i=1;i<n;i++){
LL x,y;cin>>x>>y;
g[x].push_back(y);g[y].push_back(x);
}
for(LL i=1;i<=n;i++) cin>>a[i];
dfs(1,0);
cout<<add[1]+del[1]<<endl;
return 0;
}