1. 程式人生 > >Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost 【DFS換根 || 樹形dp】

Codeforces Round #527 (Div. 3) F. Tree with Maximum Cost 【DFS換根 || 樹形dp】

傳送門:http://codeforces.com/contest/1092/problem/F

F. Tree with Maximum Cost

time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output

You are given a tree consisting exactly of nn vertices. Tree is a connected undirected graph with 

n−1">n1n−1 edges. Each vertex vv of this tree has a value avav assigned to it.

Let dist(x,y)dist(x,y) be the distance between the vertices xx and yy. The distance between the vertices is the number of edges on the simple path between them.

Let's define the cost of the tree as the following value: firstly, let's fix some vertex of the tree. Let it be vv. Then the cost of the tree is i=1ndist(i,v)ai∑i=1ndist(i,v)⋅ai.

Your task is to calculate the maximum possible cost of the tree if you can choose 

v">vv arbitrarily.

Input

The first line contains one integer nn, the number of vertices in the tree (1n21051≤n≤2⋅105).

The second line of the input contains nn integers a1,a2,,ana1,a2,…,an (1ai21051≤ai≤2⋅105), where aiai is the value of the vertex ii.

Each of the next n1n−1 lines describes an edge of the tree. Edge ii is denoted by two integers uiui and vivi, the labels of vertices it connects (1ui,vin1≤ui,vi≤n, uiviui≠vi).

It is guaranteed that the given edges form a tree.

Output

Print one integer — the maximum possible cost of the tree if you can choose any vertex as vv.

Examples input Copy
8
9 4 1 7 10 1 6 5
1 2
2 3
1 4
1 5
5 6
5 7
5 8
output Copy
121
input Copy
1
1337
output Copy
0
Note

Picture corresponding to the first example:

You can choose the vertex 33 as a root, then the answer will be 29+14+01+37+310+41+46+45=18+4+0+21+30+4+24+20=1212⋅9+1⋅4+0⋅1+3⋅7+3⋅10+4⋅1+4⋅6+4⋅5=18+4+0+21+30+4+24+20=121.

In the second example tree consists only of one vertex so the answer is always 00.

 

題意概括:

給出一棵 有 N 個結點 N-1 條邊的樹,每個結點的權值為 a[ i ], 每條邊的邊權為 1 .

每一點的貢獻 = 該點的深度 * 該點的權值。

所以以不同的點作為 整棵樹的根 會得到不同的樹結點的貢獻總和。

求最大的樹結點的貢獻組合。

 

解題思路:

一、樹的換根 兩次DFS

跑第一次DFS,處理出 Sum[ u ] 以 u 為根的子樹的貢獻總和(包括 u 結點本身),處理出以 結點1為根 的樹的貢獻總和 res;

第二次 DFS 換根:

假設 fa = 1, u = 5(即從根為1 轉換為根為 5)

由上圖可以發現 紅色部分的每一個結點都會與 根:u 多連了一條邊 ,即紅色部分的貢獻要加倍(相當於深度+1,所有紅色部分結點貢獻)。

而紅色部分就是 以 u 為根的子樹之外的結點:即 ( Sum[ fa ] - Sum[ u ] );

藍色部分的所有結點 都會與 根 u 少連一條邊,即深度-1,藍色部分結點貢獻和減半;

以 fa = 1 為根時,總貢獻和為 res;

轉換為以 u = 5 為根時,總貢獻和為 res + ( Sum[ fa ] - Sum[ u ]) - Sum[ u ];

當 u = 5 為根之後,

Sum[ fa ] = Sum[ fa ] - Sum[ u ] (即紅色部分)因為樹根變了,所以原本父親的子樹不再是整棵樹,而是原來 以 u 為根的子樹之外的結點。

Sum[ u ] = res; u 成為整棵樹的根,整棵樹都是 u 的子樹。

按照這種方式遞迴搜尋更新,取最大的res;

遞迴返回後,還原 Sum[ fa ], Sum[ u ], res 再搜尋下一個兒子結點;

 

AC code:

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #define FOR(x, maxx) for(x = 1; x <= maxx; x++)
 5 #define ZERO(aa, x) memset(aa, x, sizeof(aa))
 6 #define INF 0x3f3f3f3f
 7 #define LL long long
 8 using namespace std;
 9 
10 const int MAXN = 2e5+10;
11 
12 struct EDGE
13 {
14     int v, nxt;
15 }edge[MAXN<<1];
16 int head[MAXN];
17 LL sum[MAXN], sumk[MAXN];
18 
19 int cost[MAXN], dep[MAXN];
20 int N, cnt;
21 LL ans, res;
22 
23 void add(int from, int to)
24 {
25     edge[cnt].v = to;
26     edge[cnt].nxt = head[from];
27     head[from] = cnt++;
28 }
29 void init()
30 {
31     memset(head, -1, sizeof(head));
32     cnt = 0;
33     ans = 0;
34 }
35 
36 void dfs1(int now, int dh, int fa)
37 {
38     //puts("zjy");ans = max(res, ans);
39     int to;
40     sum[now] = cost[now];
41     res += 1LL*cost[now]*dh;
42 //    dep[now] = dh;
43 //    f[now] = fa;
44     //printf("now: %d\n", now);
45     for(int i = head[now]; i != -1; i = edge[i].nxt){
46         to = edge[i].v;
47         if(to != fa){
48             dfs1(to, dh+1, now);
49             sum[now]+=sum[to];
50         }
51     }
52 }
53 
54 void dfs2(int now, int fa)
55 {
56     ans = max(res, ans);
57     int to;
58     LL a, b, c;
59     for(int i = head[now]; i != -1; i = edge[i].nxt){
60         to = edge[i].v;
61         if(to == fa) continue;
62         a = sum[now], b = sum[to], c = res;
63         res-=sum[to];               //當前子樹的節點距離-1
64         res+=sum[now]-sum[to];      //當前非子樹節點距離+1
65         sum[now]-=sum[to];
66         sum[to] = a;
67         dfs2(to, now);
68         sum[now] = a;   //還原
69         sum[to] = b;
70         res = c;
71     }
72 }
73 
74 int main()
75 {
76     int i, j;
77     init();
78     scanf("%I64d", &N);
79     FOR(i, N) scanf("%I64d", &cost[i]);
80     int u, v;
81     for(i = 1; i < N; i++){
82         scanf("%d %d", &u, &v);
83         add(u, v);
84         add(v, u);
85     }
86     //puts("zjy");
87     dfs1(1, 0, 1);      //第一次遞迴求初始值
88     dfs2(1, 1);
89     printf("%I64d\n", ans);
90     return 0;
91 }

 

 

 

二、樹形dp

同樣需要一次DFS 預處理出 s[ u ] 以 u 為根的子樹的貢獻總和(包括 u 結點本身);

狀態:dp[ u ]  以 u 為根時,整棵樹的貢獻和

狀態轉移:dp[u] = dp[fa] + sum - 2*s[u]; ( sum 為所有結點的權值總和)

假設 fa = 1,u = 5;

dp[ 5 ] = dp[ 1 ] + 紅色 - 藍色 - cost[ u ];

dp[ 5 ] = dp[ 1 ] + ( sum - s[ 5 ]) - s[ 5 ];

dp[ 5 ] = dp[ 1 ]  + sum - 2*s[ 5 ];

 

AC code:

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #define FOR(x, maxx) for(x = 1; x <= maxx; x++)
 5 #define ZERO(aa, x) memset(aa, x, sizeof(aa))
 6 #define INF 0x3f3f3f3f
 7 #define LL long long
 8 using namespace std;
 9 
10 const int MAXN = 2e5+10;
11 
12 struct EDGE
13 {
14     int v, nxt;
15 }edge[MAXN<<1];
16 int head[MAXN], cnt;;
17 LL sum[MAXN], dp[MAXN];
18 LL cost[MAXN];
19 LL ans, res;
20 LL SSum;
21 int N;
22 
23 void add(int from, int to)
24 {
25     edge[cnt].v = to;
26     edge[cnt].nxt = head[from];
27     head[from] = cnt++;
28 }
29 void init()
30 {
31     memset(head, -1, sizeof(head));
32     memset(dp, 0, sizeof(dp));
33     SSum = 0LL;
34     cnt = 0;
35     ans = 0;
36 }
37 
38 void dfs(int now, int fa)
39 {
40     int to;
41     sum[now] = cost[now];
42     for(int i = head[now]; i != -1; i = edge[i].nxt){
43         to = edge[i].v;
44         if(to == fa) continue;
45         dfs(to, now);
46         sum[now]+=sum[to];
47         dp[now] = dp[now] + dp[to] + sum[to];
48     }
49 }
50 
51 void solve(int now, int fa)
52 {
53     int to;
54     if(fa) dp[now] = dp[fa]+SSum-2*sum[now];
55     for(int i = head[now]; i != -1; i = edge[i].nxt){
56         to = edge[i].v;
57         if(to == fa) continue;
58         solve(to, now);
59     }
60     ans = max(ans, dp[now]);
61 }
62 
63 int main()
64 {
65     init();
66     scanf("%d", &N);
67     for(int i = 1; i <= N; i++){
68         scanf("%I64d", &cost[i]);
69         SSum+=cost[i];
70     }
71     int u, v;
72     for(int i = 1; i < N; i++){
73         scanf("%d %d", &u, &v);
74         add(u, v);
75         add(v, u);
76     }
77     //puts("zjy");
78     dfs(1, 0);      //第一次遞迴求初始值
79     solve(1, 0);
80     printf("%I64d\n", ans);
81     return 0;
82 }