首页 > 代码库 > Codeforces Round #168 (Div. 1) B. Zero Tree 树形dp
Codeforces Round #168 (Div. 1) B. Zero Tree 树形dp
题目链接:
http://codeforces.com/problemset/problem/274/B
题意:
给出一棵树,每个点有权值,每次操作可以对一个联通子集中的点全部加1,或者全部减1,且每次操作必须包含点1,问最少通过多少次操作可以让整棵树每个点的权值变为0.
思路:
http://blog.csdn.net/qq_24451605/article/details/48622953
- 定义状态up[u],down[u]代表点u被加操作的次数和点u被减操作的次数
- 因为必须包含点1,所以我们将树的根定在点1,那么对于每一点的子树中点,如果要修改的话,那么一定会经过当前这个点,因为这是通向根的必经之路。
- 所以对于每个点u,它被加修改和减修改的次数,就是它的儿子中进行该操作的最大次数,因为如果有两个儿子都需要进行该操作,那么完全可以两步并一步,所以只需要取最大值就可以了。
- 那么也就是 up[u]=max(up[u],up[v]) v是u的子节点
- down[u]同理。
- 因为每次修改一定会修改点1,所以最后答案就是up[1]+down[1]
神奇啊,这样的转换 我想不到啊...orz,1这个根节点连接所有的修改,父节点修改的次数是子节点的最大值,这样就把联通子集通过1神奇的联系在一起。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 #define MS(a) memset(a,0,sizeof(a)) 5 #define MP make_pair 6 #define PB push_back 7 const int INF = 0x3f3f3f3f; 8 const ll INFLL = 0x3f3f3f3f3f3f3f3fLL; 9 inline ll read(){ 10 ll x=0,f=1;char ch=getchar(); 11 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} 12 while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} 13 return x*f; 14 } 15 ////////////////////////////////////////////////////////////////////////// 16 const int maxn = 1e5+10; 17 18 vector<int> g[maxn]; 19 int n; 20 ll a[maxn],up[maxn],down[maxn]; 21 22 void dfs(int u,int fa){ 23 for(int i=0; i<(int)g[u].size(); i++){ 24 int v = g[u][i]; 25 if(v == fa) continue; 26 dfs(v,u); 27 up[u] = max(up[u],up[v]); 28 down[u] = max(down[u],down[v]); 29 } 30 a[u] = a[u]+up[u]-down[u]; 31 if(a[u] > 0) down[u] += a[u]; 32 else up[u] -= a[u]; 33 } 34 35 int main(){ 36 cin >> n; 37 for(int i=1; i<n; i++){ 38 int u,v; cin>>u>>v; 39 g[u].push_back(v); 40 g[v].push_back(u); 41 } 42 for(int i=1; i<=n; i++) 43 cin >> a[i]; 44 dfs(1,-1); 45 46 cout << up[1]+down[1] << endl; 47 48 return 0; 49 }
Codeforces Round #168 (Div. 1) B. Zero Tree 树形dp
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。