树的基础
树的直径
-
定义:树上最长的简单路径。(可能有多条)
-
树的直径的性质
- 直径两端点一定是两个叶子节点。
- 距离任意点最远的点一定是直径的一个端点,这个基于贪心求直径方法的正确性可以得出。
- 对于两棵树,如果第一棵树直径两端点为
,第二棵树直径两端点为 ,用条边将两棵树连接,那么新树的直径一定是 中的两个点。 - 对于一棵树,如果在一个点上接一个叶子节点,那么最多会改变直径的一个端点。
- 若一棵树存在多条直径,那么这些直径交于一点且交点是这些直径的中点。(奇数时中间为一条边)
- 求法
-
两次 dfs,第一次从任意一个节点出发,找直径的一个端点,第二次找另一端点。
代码:
void dfs(int u,int fa){ pre[u]=fa; for(int i=head[u];i;i=e[i].nex){ int v=e[i].to; if(v==fa) continue; dis[v]=dis[u]+e[i].w; if(dis[v]>dis[l]) l=v; dfs(v,u); } } void dfs1(int u,int fa){ for(int i=head[u];i;i=e[i].nex){ int v=e[i].to; if(v==fa||vis[v]) continue; d[v]=d[u]+e[i].w; if(d[v]>ans1) ans1=d[v]; dfs1(v,u); } }
-
树形 dp,
,直径为 。(先更新 )代码:
void dfs(int u,int fa){ for(int i=head[u];i;i=e[i].nex){ int v=e[i].to; if(v==fa) continue; dfs(v,u); if(dis[v]+dis[u]+e[i].w>ans1) l=v; ans1=max(ans1,dis[v]+dis[u]+e[i].w); if(dis[v]+e[i].w>dis[u]) nx[u]=v; dis[u]=max(dis[u],dis[v]+e[i].w); } }
例题
-
树的直径重复边计数
利用直径性质,找出直径后将一条直径上的点标记,将直径上的点作为根节点出发,若不经过直径上的点得到的最大长度与该点到直径左端点和右端点的距离相等,说明一端的点就不是必须点,减去即可。
树的重心
- 定义:树的重心,又名树的质心,删去重心后,最大子树的节点数最小。
- 性质:
- 以树的重心为根,那么根节点的每棵子树的大小都小于等于
- 每棵子树的大小都小于等于
的点一定是这棵树的重心。(即性质1的逆命题) - 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个重心, 他们的距离和一样。
- 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
- 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
- 一棵树最多有两个重心,且相邻。
- 求法:
一次 dfs 记录每个子树的大小,以及以每个点作为根节点时最大子树的节点数,取最小值记录即可。
例题
-
根据重心性质,求重心模板题。
void dfs(ll u,int fa){ siz[u]=1,mson[u]=0; for(int i=head[u];i;i=e[i].nex){ ll ev=e[i].to; if(ev==fa) continue; dfs(ev,u); siz[u]+=siz[ev]; mson[u]=max(mson[u],siz[ev]); } if(ans>max(n-siz[u],mson[u])){ ans=max(n-siz[u],mson[u]); k=u; } else if(ans==max(n-siz[u],mson[u])) k=min(k,u); }
树的中心
不是很常见。
- 定义:以树的中心为整棵树的根时,从该根到每个叶子节点的最长路径最短。
- 性质:树的中心一定在树的直径上,且趋于中点。
- 求法:求出直径取中点即可。(可以树形 dp,
但是不会
本文作者:dayz-break
本文链接:https://www.cnblogs.com/dayz-break/p/18341250
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步