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 }