树的直径与重心

\(\text{Part I: 树的直径}\)

定义:树上最远的两个点。

大致做法:两次 \(\text{dfs}\) .

第一次我们对任意一个节点跑 \(\text{dfs}\) 求出书上离他最远的点设为 q 则下一次我们以 q 为根再跑一遍 \(\text{dfs}\) 到达另一个最远结点。最后可以证明这两个节点就是树的直径。

代码:

//树的直径
void dfs(int u, int fa) {//u: 当前搜索到的节点 fa 这个节点的父亲节点
  for (int v : E[u]) {//枚举出边
    if (v == fa) continue;//如果这个出边回到他的父亲,那么过掉
    d[v] = d[u] + 1;
    if (d[v] > d[c]) c = v;//更新
    dfs(v, u);//继续往下跑
  }
}

\(\text{Part II: 树的重心}\)

大致做法:继续跑 \(\text{dfs}\) .

\(\text{dfs}\) 中计算每个子树的大小,记录“向下”的子树的最大大小,利用总点数 - 当前子树(这里的子树指有根树的子树)的大小得到“向上”的子树的大小,然后就可以依据定义找到重心了。

但其实这句话不好理解,感觉看代码会好很多。

//树的重心
void GetCentroid(int cur, int fa) { //cur 和 fa 分别是当前搜索到的节点和父亲节点
  size[cur] = 1;//无论如何这个节点的大小至少是1吧
  weight[cur] = 0;//暂时初始化的时候这个点没有子树
  for (int i = head[cur]; i != -1; i = e[i].nxt) {//开始遍历
    if (e[i].to != fa) {  // e[i].to 表示这条有向边所通向的节点。
      GetCentroid(e[i].to, cur);//继续往下搜索
      size[cur] += size[e[i].to];//因为有子树了嘛,所以大小要直接加上他那个子树的大小
      weight[cur] = max(weight[cur], size[e[i].to]);//取个最大值算重量
    }
  }
  weight[cur] = max(weight[cur], n - size[cur]);//不好解释,就是相当于有一部分会没有被记录到,即这个节点“向上看”的子树,然而也就这一个没有记录到,因此我们直接把当前的最大值和 n减去当前的大小做过比较就可以求出来了。
  if (weight[cur] <= n / 2) {  // 依照树的重心的定义统计
    centroid[centroid[0] != 0] = cur;
  }
}
posted @ 2021-09-23 08:53  zplqwq  阅读(71)  评论(0编辑  收藏  举报