CF 1281E Jeremy Bearimy 树的性质

抄完题解之好像不难系列

求最小,用dfs,尽可能选择相邻的点对,如果以某个儿子为顶点的子树sz为odd,则儿子v与当前节点u所连的边必取,儿子v的其余点两两相连1,与u无关。

因此,只需要统计子树大小为奇数的v与其父亲u之间这条边的权值。

求最大,同样基于dfs。画一张图我们可以看出来,越靠近“中间”的边被统计的次数更多。然后我们发现,最”中间“的点实质上是树的重心。

对于某一条边u——v,以这条边为界将整棵树一分为二,sz[u]和sz[v]的较小值即是这条边在统计中至多被取到的次数。

假设有一对点位于u——v的同侧,那么通过交换,我们发现,位于两侧势必能获得更大的权值和。

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const int N = 1e5 + 10;
 5 
 6 ll ans1, ans2, k; 
 7 ll head[N << 1], sz[N << 1], tot;
 8 
 9 struct edge
10 {
11     int to, next;
12     ll w;
13 }e[N << 2];
14 
15 inline void add_edge(int u, int v, ll w)
16 {
17     e[tot].next = head[u];
18     e[tot].to = v;
19     e[tot].w = w;
20     head[u] = tot++;
21 }
22 
23 void dfs(int u, int f)
24 {
25     sz[u] = 1;
26     for(int i = head[u] ; ~i ; i = e[i].next){
27         int v = e[i].to;
28         if(v == f)    continue;
29         ll w = e[i].w;
30         dfs(v, u);
31         sz[u] += sz[v];
32         if(sz[v] % 2 == 1)    ans1 += w;
33         //如果以某个儿子为顶点的子树sz为odd,则儿子v与当前节点u所连的边必取
34         ans2 += 1ll * w * min(sz[v], 2ll * k - sz[v]);//
35     }
36 }
37 
38 int main(){
39     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
40     int t; cin >> t;
41     while(t--)
42     {
43         cin >> k;
44         for(int i = 0 ; i < (N << 1) ; i++){
45             head[i] = -1, sz[i] = 0;
46         }tot = ans1 = ans2 = 0;
47         for(int i = 1 ; i <= (k * 2 - 1) ; i++){
48             int u, v; ll val;
49             cin >> u >> v >> val;
50             add_edge(u, v, val);
51             add_edge(v, u, val);
52         }
53         dfs(1, -1);
54         cout << ans1 << " " << ans2 << endl;
55     }
56     
57     return 0;
58 }

 学习途径:https://www.cnblogs.com/Anonytt/p/13623738.html

原文地址:https://www.cnblogs.com/ecustlegendn324/p/14364308.html