树的直径(Diameter)是指树上的最长简单路。
直径的求法:两遍搜索 (BFS or DFS)
任选一点w为起点,对树进行搜索,找出离w最远的点u。
以u为起点,再进行搜索,找出离u最远的点v。则u到v的路径长度即为树的直径。
简单证明:
如果w在直径上,那么u一定是直径的一个端点。反证:若u不是端点,则从直径另一端点到w再到u的距离比直径更长,与假设矛盾。
如果w不在直径上,且w到其距最远点u的路径与直径一定有一交点c,那么由上一个证明可知,u是直径的一个端点。
如果w到最远点u的路径与直径没有交点,设直径的两端为S与T,那么(w->u)>(w->c)+(c->T),推出(w->u)+(S->c)+(w->c)>(S->c)+(c->T)=(S->T)与假设矛盾。
因此w到最远点u的路径与直径必有交点。
S-----------c-----------T
|
w------u
树的重心
何谓重心
树的重心:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
树的重心可以通过简单的两次搜索求出,第一遍搜索求出每个结点的子结点数量son[u],第二遍搜索找出使max{son[u],n-son[u]-1}最小的结点。
实际上这两步操作可以在一次遍历中解决。对结点u的每一个儿子v,递归的处理v,求出son[v],然后判断是否是结点数最多的子树,处理完所有子结点后,判断u是否为重心。
1 struct CenterTree{ 2 int n; 3 int ans; 4 int siz; 5 int son[maxn]; 6 void dfs(int u,int pa){ 7 son[u]=1; 8 int res=0; 9 for (int i=head[u];i!=-1;i=edges[i].next){ 10 int v=edges[i].to; 11 if (v==pa) continue; 12 if (vis[v]) continue; 13 dfs(v,u); 14 son[u]+=son[v]; 15 res=max(res,son[v]-1); 16 } 17 res=max(res,n-son[u]); 18 if (res<siz){ 19 ans=u; 20 siz=res; 21 } 22 } 23 int getCenter(int x){ 24 ans=0; 25 siz=INF; 26 dfs(x,-1); 27 return ans; 28 } 29 }Cent;