首页 > 代码库 > 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