1. 程式人生 > >Codeforces Round #168 (Div. 1) B. Zero Tree 樹形dp

Codeforces Round #168 (Div. 1) B. Zero Tree 樹形dp

max cin 只需要 進行 push ack sin turn its

題目鏈接:

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