Розглянемо спрощений варіант задачі. Нехай всі вершини дерева зафарбовані в один колір. Оголосимо стек, який спочатку пустий. Запустимо обхід у глибину з кореня дерева. При вході у вершину додамо її в стек, а при завершенні обробки вершини видалимо верхній елемент стека (ним якраз і буде вершина ). Після завершення обходу стек знову стане порожнім.
Запитання: що буде знаходитися на вершині стека в момент входу у вершину , до того як ми помістимо в стек?
Тепер перейдемо до розв'язання вихідної задачі. Створимо стеків — по одному для кожного кольору (наприклад, можна використовувати вектор стеків). Спочатку всі стеки пусті. Запустимо обхід у глибину з кореня дерева — вершини . Обробка вершини , зафарбованої в колір , включає наступні кроки:
Якщо стек s[color] не пустий, то на його вершині знаходиться номер вершини, яка є найближчим предком вершини того ж кольору. Якщо стек пустий, то такої вершини не існує, і в якості відповіді для вершини слід вивести .
Додаємо вершину в стек s[color].
Рекурсивно запускаємо пошук у глибину від всіх синів вершини .
Після завершення обробки видаляємо вершину зі стека s[color].
Під час обходу у глибину, перебуваючи у вершині , у стеках міститься інформація про кольори всіх вершин, розташованих на єдиному шляху від кореня дерева до вершини . Зокрема, стек 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:])