Find the centroid#10158

Find the centroid (Editorial)

Consider a tree with vertices.

A centroid of a tree is a vertex whose removal results in splitting the tree into connected components, each containing no more than vertices.

The task is to find all centroids of the tree.

Let’s consider examples of trees and their centroids (marked in green).

Run a depth-first search starting from vertex , which will calculate the number of vertices in the subtree rooted at vertex and store this value in . For the given examples, we will indicate the corresponding value of next to each vertex . In all examples, the depth-first search starts from vertex .

If vertex has children to_1, to_2, ..., to_k, then

sub_v = 1 + sub_{to_1} + sub_{to_2} + ... + sub_{to_k}

Vertex is a centroid of the tree if and only if:

For example, in the tree from the right with n = 5 vertices, vertex is a centroid because:

Example

Let's consider the tree from the example. Vertex is a centroid.

Algorithm implementation

We’ll store the graph in the adjacency list .

vector<vector<int> > g;

The dfs function returns the number of vertices in the subtree rooted at vertex and saves this value in .

int dfs(int v, int p = -1)
{
  sub[v] = 1;
  for (int to : g[v])
    if (to != p) sub[v] += dfs(to, v);
  return sub[v];
}

The centroid function performs a depth-first search, finds the centroids, and stores them in the array.

void centroid(int v, int p = -1)
{

Set flag = 1 if vertex is a centroid.

  int flag = 1;

Iterate through the vertices , adjacent to . Consider an edge v → to.

  for (int to : g[v])
    if (to != p)
    {

If for a child it holds that sub[to] > n / 2, then is not a centroid.

      if (sub[to] > n / 2) flag = 0;

If for a child it holds that sub[to] < n / 2, then the subtree rooted at does not contain centroids. It makes sense to continue the search in the child only if sub[to] \ge n / 2.

      if (sub[to] >= n / 2) centroid(to, v);
    }

The tree without the subtree rooted at contains n - sub[v] vertices. If it contains more than vertices, then is not a centroid.

  if (n - sub[v] > n / 2) flag = 0;

If vertex satisfies all the conditions of a centroid, add it to the array.

  if (flag) centr.push_back(v);
}

The main part of the program. Read the input data and initialize the arrays.

scanf("%d", &n);
g.resize(n + 1);
sub.resize(n + 1);

Read the input graph.

for (i = 0; i < n - 1; i++)
{
  scanf("%d %d", &a, &b);
  g[a].push_back(b);
  g[b].push_back(a);
}

Run a depth-first search and find the centroids of the tree.

dfs(1);
centroid(1);

Print one of the centroids.

printf("%d\n", centr[0]);

Algorithm implementation – second solution

Let’s run a depth-first search , that will compute the following values for each vertex :

Then, the centroid will be the vertex with the smallest value of . There may be either one or two such vertices in the tree.

Vertex v = 6 is connected to subtrees, with sizes and respectively. The value f[6] = 5 contains the maximum size of a subtree.

At the same time, the smallest value in the array is f[6] = 5. Therefore, vertex is the centroid.

Let’s consider the labeling of a tree with two centroids.

Two vertices have the smallest value in the array : f[1] = 3 and f[2] = 3.

void dfs(int v, int p = -1)
{
  sub[v] = 1;
  f[v] = 0;
  for (int to : g[v])
    if (to != p)
    {
      dfs(to, v);
      sub[v] += sub[to];
      f[v] = max(f[v], sub[to]);
    }
  f[v] = max(f[v], n - sub[v]);
  if ((root == 0) || (f[v] < f[root])) root = v;
}

The main part of the program. Read the input data and initialize the arrays.

scanf("%d", &n);
g.resize(n + 1);
f.resize(n + 1);
sub.resize(n + 1);
for (i = 0; i < n - 1; i++)
{
  scanf("%d %d", &a, &b);
  g[a].push_back(b);
  g[b].push_back(a);
}

Run a depth-first search and print one of the centroids of the tree.

root = 0;
dfs(1);
printf("%d\n", root);

Python implementation

Increase the size of the stack.

import sys
sys.setrecursionlimit(300000)

The dfs function returns the number of vertices in the subtree rooted at vertex and saves this value in .

def dfs(v, p = -1):
  sub[v] = 1
  for to in g[v]:
    if to != p:
      sub[v] += dfs(to, v)
  return sub[v]

The centroid function performs a depth-first search, finds the centroids, and stores them in the array.

def find_centroid(v, p = -1):

Set flag = True if vertex is a centroid.

  flag = True

Iterate through the vertices , adjacent to . Consider an edge v → to.

  for to in g[v]:
    if to == p: continue

If for a child it holds that sub[to] > n / 2, then is not a centroid.

    if sub[to] > n // 2:
      flag = False

If for a child it holds that sub[to] < n / 2, then the subtree rooted at does not contain centroids. It makes sense to continue the search in the child only if sub[to] \ge n / 2.

    if sub[to] >= n // 2:
      find_centroid(to, v)

The tree without the subtree rooted at contains n - sub[v] vertices. If it contains more than vertices, then is not a centroid.

  if n - sub[v] > n // 2:
      flag = False

If vertex satisfies all the conditions of a centroid, add it to the array.

  if flag: centr.append(v)

The main part of the program. Read the input data and initialize the arrays.

n = int(input())
g = [[] for _ in range(n + 1)]
sub = [0] * (n + 1)
centr = []

Read the input graph.

for i in range(n - 1):
  a, b = map(int, input().split())
  g[a].append(b)
  g[b].append(a)

Run a depth-first search and find the centroids of the tree.

dfs(1)
find_centroid(1)

Print one of the centroids.

print(centr[0])

Python implementation – second solution

Increase the size of the stack.

import sys
sys.setrecursionlimit(200000)

Let’s run a depth-first search , that will compute the following values for each vertex :

Then, the centroid will be the vertex with the smallest value of . There may be either one or two such vertices in the tree.

def dfs(v, p=-1):
  global root
  sub[v] = 1
  f[v] = 0
  for to in g[v]:
    if to != p:
      dfs(to, v)
      sub[v] += sub[to]
      f[v] = max(f[v], sub[to])
  f[v] = max(f[v], n - sub[v])
  if root == 0 or f[v] < f[root]:
    root = v

The main part of the program. Read the input data and initialize the arrays.

n = int(input())
g = [[] for _ in range(n + 1)]
f = [0] * (n + 1)
sub = [0] * (n + 1)

for _ in range(n - 1):
  a, b = map(int, input().split())
  g[a].append(b)
  g[b].append(a)

Run a depth-first search and print one of the centroids of the tree.

root = 0
dfs(1)
print(root)