B - Tree 2

30 pts? 60 pts!

Idea: for all nodes, “climbing” to the root

This algo actually earns 60 pts!

Because the tree generated randomly using \(p_i = \text{rand}(1, i-1)\) has an expected depth of \(\mathcal{O}(\log n)\).

The proof will be at the end of the solution.

  • #include <functional>
    #include <iostream>
    #include <vector>
    
    class Solution {
     public:
      static std::vector<int> solve(int n, const std::vector<int> &parents) {
        std::vector<int> depth;
        for (int i = 0; i < n; ++i) {
          int current = i;
          int d = 0;
          while (current != 0) {
            current = parents[current - 1] - 1;
            d++;
          }
          depth.push_back(d);
        }
        return depth;
      }
    };
    
    int main() {
      std::ios::sync_with_stdio(false);
      std::cin.tie(nullptr);
      std::cout.tie(nullptr);
      int n;
      std::cin >> n;
      std::vector<int> parents;
      parents.reserve(n - 1);
      for (int i = 1; i < n; ++i) {
        int parent;
        std::cin >> parent;
        parents.push_back(parent);
      }
      auto result = Solution::solve(n, parents);
      for (int i = 0; i < n; ++i) {
        std::cout << result[i] << " ";
      }
      return 0;
    }
    

100 pts

DFS and BFS is a good idea for most of the tree problem, because this method allows the entire tree to be traversed in a specific order.

In both types of search, the parent node always arrives before the child nodes.

So, if we let \(d_i\) be the distance of node \(i\), during our DFS/BFS, \(d_{p_i}\) is always computed before \(d_i\).

Here is the pseudocode for DFS:

Here is the pseudocode for BFS:

  • import sys
    
    sys.setrecursionlimit(10**8)
    
    
    class Solution:
        @staticmethod
        def solve(n: int, parents: list[int]) -> list[int]:
            children = [[] for _ in range(n)]
            for i in range(1, n):
                children[parents[i - 1] - 1].append(i)
            depth = [0] * n
    
            def dfs(u: int):
                for v in children[u]:
                    depth[v] = depth[u] + 1
                    dfs(v)
    
            dfs(0)
            return depth
    
    
    if __name__ == "__main__":
        n = int(input())
        parents = list(map(int, input().split()))
        depth = Solution.solve(n, parents)
        print(*depth)
    
  • #include <stdio.h>
    #include <stdlib.h>
    
    void solve(int n, int *parents, int *depth);
    
    int main() {
      int n;
      scanf("%d", &n);
      int *parents = (int *)calloc(n - 1, sizeof(int));
      for (int i = 1; i < n; ++i) {
        scanf("%d", &parents[i - 1]);
      }
      int *depth = (int *)calloc(n, sizeof(int));
      solve(n, parents, depth);
      for (int i = 0; i < n; ++i) {
        printf("%d ", depth[i]);
      }
      printf("\n");
      free(depth);
      free(parents);
      return 0;
    }
    
    #define INIT_CAPACITY 5
    
    struct ArrayList {
      int num;
      int capacity;
      int *data;
    };
    
    void array_list_init(struct ArrayList *array_list) {
      array_list->num = 0;
      array_list->capacity = INIT_CAPACITY;
      array_list->data = (int *)calloc(array_list->capacity, sizeof(int));
    }
    
    void array_list_add(struct ArrayList *array_list, int value) {
      if (array_list->num == array_list->capacity) {
        array_list->capacity *= 2;
        array_list->data =
            (int *)realloc(array_list->data, array_list->capacity * sizeof(int));
      }
      array_list->data[array_list->num++] = value;
    }
    
    void array_list_free(struct ArrayList *array_list) { free(array_list->data); }
    
    #undef INIT_CAPACITY
    
    void dfs(int u, struct ArrayList *children, int *depth) {
      for (int i = 0; i < children[u].num; ++i) {
        depth[children[u].data[i]] = depth[u] + 1;
        dfs(children[u].data[i], children, depth);
      }
    }
    
    void solve(int n, int *parents, int *depth) {
      struct ArrayList *children =
          (struct ArrayList *)calloc(n, sizeof(struct ArrayList));
      for (int i = 0; i < n; ++i) {
        array_list_init(&children[i]);
      }
      for (int i = 1; i < n; ++i) {
        array_list_add(&children[parents[i - 1] - 1], i);
      }
      dfs(0, children, depth);
      for (int i = 0; i < n; ++i) {
        array_list_free(&children[i]);
      }
      free(children);
    }
    
  • #include <functional>
    #include <iostream>
    #include <vector>
    
    class Solution {
     public:
      static std::vector<int> solve(int n, const std::vector<int> &parents) {
        std::vector<std::vector<int>> children(n);
        for (int i = 1; i < n; ++i) {
          children[parents[i - 1] - 1].push_back(i);
        }
        std::vector<int> depth(n, 0);
        std::function<void(int)> dfs = [&](int u) {
          for (int v : children[u]) {
            depth[v] = depth[u] + 1;
            dfs(v);
          }
        };
        dfs(0);
        return depth;
      }
    };
    
    int main() {
      std::ios::sync_with_stdio(false);
      std::cin.tie(nullptr);
      std::cout.tie(nullptr);
      int n;
      std::cin >> n;
      std::vector<int> parents;
      parents.reserve(n - 1);
      for (int i = 1; i < n; ++i) {
        int parent;
        std::cin >> parent;
        parents.push_back(parent);
      }
      auto result = Solution::solve(n, parents);
      for (int i = 0; i < n; ++i) {
        std::cout << result[i] << " ";
      }
      std::cout << std::endl;
      return 0;
    }
    
  • import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    
    class Solution {
      static void dfs(int u, List<Integer>[] children, int[] depth) {
        for (int v : children[u]) {
          depth[v] = depth[u] + 1;
          dfs(v, children, depth);
        }
      }
    
      static int[] solve(int n, int[] parents) {
        int[] depth = new int[n];
        @SuppressWarnings("unchecked") List<Integer>[] children = new ArrayList[n];
        for (int i = 0; i < n; i++) {
          children[i] = new ArrayList<>();
        }
        for (int i = 1; i < n; i++) {
          children[parents[i - 1] - 1].add(i);
        }
        dfs(0, children, depth);
        return depth;
      }
    
      public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] parents = new int[n - 1];
        for (int i = 1; i < n; i++) {
          parents[i - 1] = scanner.nextInt();
        }
        int[] depth = solve(n, parents);
        for (int i = 0; i < n; i++) {
          System.out.print(depth[i] + " ");
        }
        scanner.close();
      }
    }
    

Proof of expected depth of \(\mathcal{O}(\log n)\)

Let

\[d_i = \mathbb{E}[\text{depth of node } i]\]

As \(p_i \sim \text{uniform}(1, i-1)\), we can have

\[\begin{aligned} d_i &= \mathbb{E}_{p\sim \text{uniform}(1, i-1)}\left[d_{p} + 1\right] \\ &= \mathbb{E}_{p\sim \text{uniform}(1, i-1)}\left[d_{p}\right] + 1 \\ &= 1 + \frac{1}{i-1}\sum_{p=1}^{i-1}d_p\\ &=1 + \frac{1}{i-1} d_{i-1} + \frac{1}{i-1} \sum_{p=1}^{i-2}d_p \end{aligned}\]

As

\[d_i = 1 + \frac{1}{i-1}\sum_{p=1}^{i-1}d_p\]

so

\[\sum_{p=1}^{i-1}d_p = (i-1)(d_p-1)\]

Thus

\[\begin{aligned} d_i&=1 + \frac{1}{i-1} d_{i-1} + \frac{1}{i-1} \sum_{p=1}^{i-2}d_p\\ &=1 + \frac{1}{i-1} d_{i-1} + \frac{1}{i-1}\cdot (i-2) (d_{i-1} - 1)\\ &=\frac{1}{i-1} + d_{i-1} = \sum_{j=1}^{i-1}\frac{1}{i} \end{aligned}\]

So

\[\mathbb{E}[\text{depth of node } i]=\sum_{j=1}^{i-1}\frac{1}{i}\]

And this is a very famous formula called Harmonic series.

It can be easily proved that

\[\mathbb{E}[\text{depth of node } i]=\sum_{j=1}^{i-1}\frac{1}{i}\le 1 + \int_1^{i-1}\frac{1}{x}\mathrm{d}x=1+\log (i-1)\]