[算法模版]树的重心和直径

[算法模版]树的重心和直径

树的重心

引自OI-WIKI

定义

以树的重心为根时,所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。

找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心。

删去重心后,生成的多棵树尽可能平衡。

性质

树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。

把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两个树的重心的路径上。

在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。

求法

树的重心可以通过简单的两次搜索求出。

  1. 第一遍搜索求出以每个节点为根的子树中结点数量 \(sz_{u}\)
  2. 第二遍搜索找出使 \(\max_{v\in\operatorname{son}(u)}\{n-sz_{u},sz_{v}\}\) 最小的节点 \(u\) 。

实际上这两步操作可以在一次遍历中解决。对节点 u 的每一个儿子 v 递归处理,然后以 \(sz_{v}\) 更新 \(u\) 的子节点子树节点数最大值,处理完所有子结点后,判断 u 是否为重心。

(代码来自叉姐)

struct CenterTree {
  int n;
  int ans;
  int siz;
  int son[maxn];
  void dfs(int u, int pa) {
    son[u] = 1;
    int res = 0;
    for (int i = head[u]; ~i; i = edges[i].next) {
      int v = edges[i].to;
      if (v == pa) continue;
      dfs(v, u);
      son[u] += son[v];
      res = max(res, son[v]);
    }
    res = max(res, n - son[u]);
    if (res < siz) {
      ans = u;
      siz = res;
    }
  }
  int getCenter(int x) {
    ans = 0;
    siz = INF;
    dfs(x, -1);
    return ans;
  }
}

参考

http://fanhq666.blog.163.com/blog/static/81943426201172472943638/

https://www.cnblogs.com/zinthos/p/3899075.html

树的直径

定义

树的直径指树上的最长简单路径

求法

随意寻找一个点\(u\)进行\(BFS\),找到的最远点一定为直径的一个端点\(s\)。再从\(s\)开始\(BFS\),找到的最远点一定是直径的另一个端点\(t\)。

证明

树的直径,树的重心,树的分冶

参考资料

OI-WIKI

树的直径,树的重心,树的分冶

树的直径

原文地址:https://www.cnblogs.com/GavinZheng/p/11865199.html

时间: 2024-09-30 07:46:47

[算法模版]树的重心和直径的相关文章

树的重心及直径

树的重心 定义:树的重心也叫树的质心.找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡. 性质 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样. 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上. 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置. 一棵树最多有两个重心,且相邻. 3.find树的重心 主要用DP思想 root为重心,f[x]记录以x为根节点其子树的最大节

树的直径、树的重心与树的点分治

树的直径 树的直径(Diameter)是指树上的最长简单路. 直径的求法:两遍搜索 (BFS or DFS) 任选一点w为起点,对树进行搜索,找出离w最远的点u. 以u为起点,再进行搜索,找出离u最远的点v.则u到v的路径长度即为树的直径. 简单证明: 如果w在直径上,那么u一定是直径的一个端点.反证:若u不是端点,则从直径另一端点到w再到u的距离比直径更长,与假设矛盾. 如果w不在直径上,且w到其距最远点u的路径与直径一定有一交点c,那么由上一个证明可知,u是直径的一个端点. 如果w到最远点u

树讲解(2)——树的输入,重心,直径

one.树的输入 1.输入每个节点父亲节点的编号 #include<vector> #include<stdio.h> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define N 100000 #define maxn 123456 using namespace std; int n,x,fa[N]; bool vis[N]

树的直径与树的重心

树的直径 树的直径是指树上的最长简单路. 直径的求法:两遍搜索 任选一点w为起点,对树进行搜索,找出离w最远的点u. 以u为起点,再进行搜索,找出离u最远的点v. 则u到v的路径长度即为树的直径. ---------------------------------------------------------------- 树的重心树的重心: 找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心. 删去重心后,生成的多棵树尽可能平衡. 树的重心可以通过简单的两次搜索求出,

树的重心(个人模版)

树的重心(树的重心定义为:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心) 1 #include<stdio.h> 2 #include<string.h> 3 #include<iostream> 4 using namespace std; 5 #define ll __int64 6 struct node 7 { 8 int from; 9 int to; 10 int w; 11 int next; 12 }e[1500000];

POJ 1655 Balancing Act[树的重心/树形dp]

Balancing Act 时限:1000ms Description Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1...N. Deleting any node from the tree yields a forest: a collection of one or more trees. Define the balance of a node to be the size of the largest tree

从零开始学建树(树的分治,树的重心)

分治算法在树的路径问题中的应用 一.树的分治算法 树的分治算法是分治思想在树型结构上的体现. 任一个具有n个节点的连通路,它的任何一棵树的树枝数为n-1 分治:除去树中的某些对象,使原树被分解成若干互不相交的部分. 分治算法分为两种:一种是点的分治,一种是边的分治 1.基于点的分治 1.选取一个点将无根树转为有根树 2.递归处理每一颗以根结点的儿子为根的子树 2.基于边的分治 1.在树中选取一条边 2. 将原有的树分成两棵不相交的树,递归处理. 可以证明在基于点的分治中,如果每次都选取树的重心,

【poj1655】【poj3107】【求树的重心】

先来说一下怎样来求树的直径: 假设 s-t这条路径为树的直径,或者称为树上的最长路 现有结论,从任意一点u出发搜到的最远的点一定是s.t中的一点,然后在从这个最远点开始搜,就可以搜到另一个最长路的端点,即用两遍广搜就可以找出树的最长路 证明: 1 设u为s-t路径上的一点,结论显然成立,否则设搜到的最远点为T则 dis(u,T) >dis(u,s) 且 dis(u,T)>dis(u,t) 则最长路不是s-t了,与假设矛盾 2 设u不为s-t路径上的点 首先明确,假如u走到了s-t路径上的一点,

求树的重心

给定一棵树,求树的重心的编号以及重心删除后得到的最大子树的节点个数size,如果size相同就选取编号最小的. 首先要知道什么是树的重心,树的重心定义为:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重 心后,生成的多棵树尽可能平衡.  实际上树的重心在树的点分治中有重要的作用, 可以避免N^2的极端复杂度(从退化链的一端出发) 算法就是跑一遍dfs,找到最优解. 链接 代码: #include <iostream> #include <string.h