Дерево#2923

Дерево (Розбір)

Виконуємо обхід дерева в глибину, починаючи з кореня. Для кожної вершини будемо зберігати множину s_i, у якій накопичуються кольори всіх вершин, що входять у піддерево з коренем в .

Якщо — син вершини в процесі обходу, то множина s_j повинна бути включена в s_i.

Кількість різних кольорів у піддереві з коренем у вершині дорівнює потужності (розміру) множини s_i.

Приклад

Ліворуч для кожної вершини вказано її колір, праворуч — множина кольорів, що містяться в піддереві з коренем у цій вершині.

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

У масиві color[i] зберігається колір вершини . У множині будемо накопичувати кольори, що зустрічаються в піддереві з коренем у вершині .

Орієнтоване дерево представлено у вигляді списку суміжності графа . У масиві зберігається кількість різних кольорів у піддереві з коренем у вершині .

#define MAX 1000010
int color[MAX], res[MAX];
set<int> s[MAX];
vector<vector<int> > g;

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

void dfs(int v)
{

Спочатку у множину додаємо колір самої вершини .

  s[v].insert(color[v]);

Перебираємо ребра дерева (v, to).

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

Для кожного ребра (v, to) додаємо множину у . Якщо розмір множини менший за розмір множини , то міняємо їх місцями. Далі вміст меншої множини додаємо у множину .

    if (s[v].size() < s[to].size()) s[v].swap(s[to]);
    s[v].insert(s[to].begin(), s[to].end());

Очищаємо множину — вона нам більше не знадобиться.

    s[to].clear();
  }

Після цього у записуємо кількість різних кольорів у піддереві, тобто розмір множини .

  res[v] = s[v].size();
}

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

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

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

dfs(0);

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

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