hdu6867 Tree // DFS

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6867

大意:给定一棵以1为固定顶点的树,你可以从任意一个父亲节点走到其儿子节点。让你新建一条边,这条边上可以双向通行。求可抵达的点对数,主意node[i]可以抵达自身node[i]。

这题一上来就想用贪心(是错的),感觉上好像挺对的。找一条最长的路径,看似可以新抵达的点更多。

实际上此题还与各节点的子树大小具有密切关系。然而我虽然发现了这一点,最后写的搜索竟然TLE。临场还是不行。。第二天写了几分钟就A了。。。

key:

考虑不加边: 每个节点可以去到的点的个数即为其自身,以及不包括自身的子树大小。设节点i的后代个数(子树大小-1)为son[i],则这些点对的个数总和就是n + ∑1,n (son[i])

第一次dfs_son()填满son[]

考虑加了一条边:首先,加边的点必定位于某条路径的末端与初始根节点1之间,这点显而易见。加边之后,收益增加的点为这条路径上除了1以外的节点。设有k个好了。那对于这k个节点中编号为i的节点,增加的点对则是  n减去包含其自身的子树大小,也即n-1-son[i]。任务就很明确了。

第二次dfs统计每条路径末端求得的∑(n-1-son[i]),取其中的最大值,再与1中总和相加即为结果

*转载请附链接 谢谢

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<string>
 5 #include<cstdio>
 6 #include<iomanip>
 7 #include<vector>
 8 #include<queue>
 9 
10 #define ll long long
11 using namespace std;
12 const int maxn = 5e5 + 10;
13 
14 int n;
15 ll ans = 0;
16 
17 int son[maxn];
18 19 
20 vector<int> v[maxn];
21 
22 int dfs_son(int now)
23 {
24     int cnt = 0;
25     ll tmp = 0;
26     for(int i = 0 ; i < v[now].size() ; i++){
27         int k = v[now][i];
28         cnt++;
29         tmp += dfs_son(k);
30     }
31     if(cnt == 0){
32         son[now] = 1;
33     }else{
34         son[now] = tmp + 1;
35     }    
36     return son[now];
37 }
38 
39 void dfs_res(int now, bool *vis, ll sum)
40 {
41     if(son[now] == 0)    ans = max(ans, sum);
42     for(int i = 0 ; i < v[now].size() ; i++){
43         int nxt = v[now][i];
44         if(vis[nxt] == true)    continue;
45         vis[nxt] = true;
46         dfs_res(nxt, vis, sum + n-1-son[nxt]);
47         vis[nxt] = false;
48     }
49     return ;
50 }
51 
52 int main(){
53     int T;scanf("%d",&T);
54     while(T--){
55         scanf("%d",&n);
56         ans = 0;
57         for(int i = 0 ; i <= n ; i++){
58             son[i] = 0;
59             v[i].clear();
60         }
61         
62         for(int i = 2 ; i <= n ; i++){
63             int father;
64             scanf("%d",&father);
65             v[father].push_back(i);//反向
66         }
67         
68         dfs_son(1);//求每个节点的儿子个数
69         
70         ll sum = 0;//所有节点的儿子和 
71         for(int i = 1 ; i <= n ; i++){
72             son[i] -= 1;
73             sum += son[i];
74         }
75         
76         bool vis[maxn];
77         vis[1] = true;
78         dfs_res(1, vis, 0);
79         
80         ans += sum + n;
81         printf("%lld
",ans);
82     }
83         
84     return 0;
85 }
原文地址:https://www.cnblogs.com/ecustlegendn324/p/13531727.html