51nod 1424:零树

51nod 1424:零树

题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1424

题目大意:有一颗大小为$n(1 \leqslant n \leqslant 10^5)$的树,根结点为$1$。现每次可以选择一个包含根结点的连通块,将此连通块内的点的值均$+1$或$-1$,问使整棵树的结点均为$0$最少需要多少次操作。

树形DP

定义状态$dp[u][j]$为将结点$u$及其子树清空至少需要$+1$和$-1$的操作数,则有

转移方程$dp[u][j]=max\{dp[v][j]\}$($v$为$u$的孩子),$dp[u][d<0]+=abs(d)$,其中$d=dp[u][1]-dp[u][0]+c[u]$为将结点$u$清空还需要的操作数.

注意答案会爆int.复杂度为$O(n)$.

代码如下:

 1 #include <cstdio>
 2 #include <vector>
 3 #include <cmath>
 4 #define N 100005
 5 using namespace std;
 6 typedef long long ll;
 7 ll dp[N][2],n,x,y,c[N];
 8 vector<int>e[N];
 9 void dfs(int u,int f){
10     for(int i=0;i<(int)e[u].size();++i){
11         int v=e[u][i];
12         if(v!=f){
13             dfs(v,u);
14             dp[u][1]=max(dp[u][1],dp[v][1]);
15             dp[u][0]=max(dp[u][0],dp[v][0]);
16         }
17     }
18     ll d=dp[u][1]-dp[u][0]+c[u];
19     dp[u][d<0]+=abs(d);
20 }
21 int main(void){
22     scanf("%lld",&n);
23     for(int i=1;i<n;++i){
24         scanf("%d%d",&x,&y);
25         e[x].push_back(y);
26         e[y].push_back(x);
27     }
28     for(int i=1;i<=n;++i)
29         scanf("%lld",&c[i]);
30     dfs(1,0);
31     printf("%lld\n",dp[1][0]+dp[1][1]);
32 }

 

posted @ 2017-04-19 17:08  barriery  阅读(314)  评论(0编辑  收藏  举报