题意:给出一棵有
n
n
n个节点的树,给我们
n
−
1
n-1
n−1条边,并给定边权,现在问以谁为根节点能够使其他节点的深度之和最大,深度意思就是该节点到根节点的简单路径的边的数量
如果想暴力求取,那么显然应该以每一个节点都作为根节点,分别计算其他节点的深度之和,再比较,找到最大的那一个,这样的话一圈
f
o
r
for
for循环加上
d
f
s
dfs
dfs,时间复杂度显然应该是
O
(
n
2
)
O(n^2)
O(n2),而题目中节点数量达到了
1
0
6
10^6
106,这种方法显然不好
那我们应该怎么求呢?
思考一下,这个算法低效的主要原因在于我们是通过每次计算节点深度之和来达到目的的,而父亲节点和子节点之间的深度实际上就差了1,所以在根发生转换的时候,我们可以简单的通过判断先后两个根节点的子节点数的变化情况来获得答案,如下图所示 设
s
z
[
i
]
sz[i]
sz[i]表示以
i
i
i为根节点的子树的节点个数,设
d
p
[
i
]
dp[i]
dp[i]表示以
i
i
i为根节点时其他子节点的深度之和
在这棵树里面,如果以1为根节点,那么其他节点的深度容易求得,那么现在把根节点换成2会怎么样呢?可以发现2的子节点和它本身的深度都减了1,而其他节点的深度都加了1,所以如果设
u
u
u为根节点,
v
v
v为子节点,那么
d
p
[
v
]
=
d
p
[
u
]
−
s
z
[
v
]
+
n
−
s
z
[
v
]
dp[v]=dp[u]-sz[v]+n-sz[v]
dp[v]=dp[u]−sz[v]+n−sz[v],所以状态转移方程为
d
p
[
v
]
=
d
p
[
u
]
+
n
−
2
∗
s
z
[
v
]
dp[v]=dp[u]+n-2*sz[v]
dp[v]=dp[u]+n−2∗sz[v]
这样我们只需要预处理出以每个节点为根节点时子节点的数目,再进行一次状态转移即可,也就是使用两个树搜索的过程,每次的时间复杂度都是
O
(
n
)
O(n)
O(n),合起来大概是
O
(
2
n
)
O(2n)
O(2n),这也就是二次扫描与换根的思想
#include<iostream>#include<cstring>#include<algorithm>#include<queue>#include<stack>#include<vector>#include<cmath>#include<cstdio>usingnamespace std;typedeflonglong ll;constint MAXN =2e6+100;constint INF =0x3f3f3f3f;
ll depth[MAXN];
ll sz[MAXN];int head[MAXN];
ll dp[MAXN];int cnt;inlineintread(){int x =0, f =1;char c =getchar();while(c <'0'|| c >'9'){if(c =='-') f =-1;c =getchar();}while(c >='0'&& c <='9'){x =(x <<1)+(x <<3)+(c ^48);c=getchar();}return x*f;}struct Edge{int next;int to;int val;}edge[MAXN];voidAdd_Edge(int u,int v,int w){
edge[cnt].next = head[u];
edge[cnt].to = v;
edge[cnt].val = w;
head[u]= cnt++;}voiddfs1(int u,int fa){
sz[u]=1;for(int i=head[u];~i;i=edge[i].next){int v = edge[i].to;if(v == fa)continue;dfs1(v, u);
sz[u]+= sz[v];}}voiddfs2(int u,int fa,int n){for(int i=head[u];~i;i=edge[i].next){int v = edge[i].to;if(v == fa)continue;
dp[v]= dp[u]+ n -2* sz[v];dfs2(v, u, n);}}intmain(){int n, u, v;
n =read();memset(head,-1,sizeof head);for(int i=1;i<n;i++){
u =read();
v =read();Add_Edge(u, v,1);Add_Edge(v, u,1);}dfs1(1,1);dfs2(1,-1, n);
ll ans =-INF;
ll dep =-INF;for(int i=1;i<=n;i++){if(dp[i]> dep){
dep = dp[i];
ans = i;}}
cout << ans;return0;}