Манкуніанець і кольорове дерево#3983

Манкуніанець і кольорове дерево (Розбір)

Розглянемо спрощений варіант задачі. Нехай всі вершини дерева зафарбовані в один колір. Оголосимо стек, який спочатку пустий. Запустимо обхід у глибину з кореня дерева. При вході у вершину додамо її в стек, а при завершенні обробки вершини видалимо верхній елемент стека (ним якраз і буде вершина ). Після завершення обходу стек знову стане порожнім.

Запитання: що буде знаходитися на вершині стека в момент входу у вершину , до того як ми помістимо в стек?

Тепер перейдемо до розв'язання вихідної задачі. Створимо стеків — по одному для кожного кольору (наприклад, можна використовувати вектор стеків). Спочатку всі стеки пусті. Запустимо обхід у глибину з кореня дерева — вершини . Обробка вершини , зафарбованої в колір , включає наступні кроки:

Під час обходу у глибину, перебуваючи у вершині , у стеках міститься інформація про кольори всіх вершин, розташованих на єдиному шляху від кореня дерева до вершини . Зокрема, стек s[color] зберігає номери вершин на цьому шляху, зафарбованих у колір . Ці вершини заносяться в стек у порядку їх відвідування при обході у глибину.

Приклад

Коли пошук у глибину дійде до вершини , із c = 4 стеків два будуть порожніми (відповідні кольорам і ).

Стек номер (для кольору ) міститиме вершину , стек номер (для кольору ) міститиме вершину . Так як вершина має колір , її найближчий предок з таким же кольором знаходиться на вершині стека номер .

Таким чином, найближчим предком вершини за кольором є вершина .

Реалізація алгоритму

Оголосимо робочі масиви.

vector<int> col, res;
vector<vector<int> > g;
vector<stack<int> > s;

Функція dfs виконує пошук у глибину, починаючи з вершини .

void dfs(int v)
{

Вершина має колір .

  int color = col[v];

Номер найближчого предка вершини , що має колір , зберігаємо в .

  if(s[color].empty())
    res[v] = -1;
  else
    res[v] = s[color].top();

Заносимо вершину в стек номер .

  s[color].push(v);

Перебираємо всіх синів вершини і рекурсивно запускаємо з них пошук у глибину.

  for (int to : g[v])
    dfs(to);

Після завершення обробки вершини (тобто при виході) видаляємо її зі стека з номером .

  s[color].pop();
}

Основна частина програми. Читаємо вхідні дані. Будуємо дерево.

scanf("%d %d", &n, &c);
g.resize(n + 1);
for(i = 2; i <= n; i++)
{
  scanf("%d",&val);
  g[val].push_back(i);
}

Читаємо кольори вершин дерева.

col.resize(n + 1);
for(i = 1; i <= n; i++)
  scanf("%d",&col[i]);

Запускаємо пошук у глибину з вершини .

s.resize(c + 1);
res.resize(n + 1);
dfs(1);

Виводимо відповідь.

for(i = 1; i <= n; i++)
  printf("%d ",res[i]);
printf("\n");

Python реалізація

Збільшуємо стек для рекурсії.

import sys
sys.setrecursionlimit(1000000)

Функція dfs виконує пошук у глибину, починаючи з вершини .

def dfs(v):

Вершина має колір .

  color = col[v]

Номер найближчого предка вершини , що має колір , зберігаємо в .

  if not s[color]:
    res[v] = -1
  else:
    res[v] = s[color][-1]

Заносимо вершину в стек номер .

  s[color].append(v)

Перебираємо всіх синів вершини і рекурсивно запускаємо з них пошук у глибину.

  for to in g[v]:
    dfs(to)

Після завершення обробки вершини (тобто при виході) видаляємо її зі стека з номером .

  s[color].pop()

Основна частина програми. Читаємо вхідні дані. Будуємо дерево.

n, c = map(int, input().split())
lst = list(map(int, input().split()))
g = [[] for _ in range(n + 1)]
for i in range(2, n + 1):
  val = lst[i - 2]
  g[val].append(i)

Читаємо кольори вершин дерева.

col = [0] * (n + 1)
col[1:] = list(map(int, input().split()))

Запускаємо пошук у глибину з вершини .

s = [[] for _ in range(c + 1)]
res = [0] * (n + 1)
dfs(1)

Виводимо відповідь.

print(*res[1:])