Mancunian və rəngli ağac#3983

Mancunian və rəngli ağac (Həll təhlili)

Təsəvvür edək ki, məsələnin sadələşdirilmiş forması mövcuddur. Gəlin ağacın bütün zirvələrinin bir rəngdə olduğunu fərz edək. Zəngin başlanğıcında boş olan bir yığın elan edək. Ağacın kökündən dərinlik üzrə axtarışa başlayaq. Zirvə -ə daxil olarkən onu yığına əlavə edəcəyik, və zirvə -ın emalı başa çatdıqda yığının üst elementini (bu zirvə olacaq) çıxaracağıq. Axtarış başa çatdıqdan sonra yığın yenə boş olacaq.

Sual: zirvəsinə daxil olarkən, yəni onu yığına yerləşdirməmişdən əvvəl yığının üzərində nə olacaq?

İndi isə əsas tapşırığın həllinə keçək. Rənglərin hər biri üçün (məsələn, yığın vektoru istifadə edərək) yığını yaradaq. Əvvəlcə bütün yığınlar boş olur. Ağacın kökündən — zirvə -dən dərinlik üzrə axtarışa başlayaq. Rəngi ilə rənglənmiş zirvəsinin emalı aşağıdakı addımlardan ibarətdir:

Ağacın dərinlik üzrə axtarışı zamanı, zirvəsində olan zaman yığınlarda kökdən zirvəsinədək olan yolda rənglər haqqında məlumatlar saxlanılır. Xüsusilə, s[color] yığını bu yolda rəngində olan zirvələrin nömrələrini saxlayır. Bu zirvələr dərinlik üzrə axtarış zamanı ziyarət edildiyindən ardıcıl olaraq yığına daxil edilir.

Nümunə

Dərinlik üzrə axtarış zirvə -ə çatanda, c = 4 yığından ikisi boş olacaq (rəngləri və olanlar).

Rəng üçün yığın zirvəsini, rəng üçün yığın zirvəsini saxlayacaq. zirvəsi rəng olduğuna görə, ona ən yaxın eyni rəngdə olan ata, rəng yığınının üstündədir.

Beləliklə, rəng üzrə zirvəsinin ən yaxın atası zirvəsidir.

Alqoritmin reallaşdırılması

İşçi massivlər elan edək.

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

dfs funksiyası zirvəsindən başlayaraq dərinlik üzrə axtarışı yerinə yetirir.

void dfs(int v)
{

Zirvə rəngi -dur.

  int color = col[v];

Rəngi olan zirvəsinin ən yaxın atası nömrəsini -də saxlayırıq.

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

zirvəsini rəng yığına əlavə edirik.

  s[color].push(v);

zirvəsinin bütün oğullarını təkrar keçir və onlardan rekursiv olaraq dərinlik üzrə axtarışı başladır.

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

Zirvə -ın emalı başa çatdıqdan sonra (çıxış zamanı) onu rəng yığından silirik.

  s[color].pop();
}

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq. Ağac qururuq.

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

Ağacın zirvələrinin rənglərini oxuyuruq.

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

Zirvə -dən başlayan dərinlik axtarışına başlayırıq.

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

Nəticəni çıxarırıq.

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

Python həyata keçirilməsi

Rekursiya üçün yığın həcmimizi artırırıq.

import sys
sys.setrecursionlimit(1000000)

dfs funksiyası zirvəsindən başlayaraq dərinlik üzrə axtarış yerinə yetirir.

def dfs(v):

Zirvə rəngi -dur.

  color = col[v]

Rəngi olan zirvəsinin ən yaxın atası nömrəsini -də saxlayırıq.

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

zirvəsini rəng yığına əlavə edirik.

  s[color].append(v)

zirvəsinin bütün oğullarını təkrar keçir və onlardan rekursiv olaraq dərinlik üzrə axtarışın başladır.

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

Zirvə -ın emalı başa çatdıqdan sonra (çıxış zamanı) onu rəng yığından silirik.

  s[color].pop()

Proqramın əsas hissəsi. Giriş məlumatlarını oxuyuruq. Ağac qururuq.

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)

Ağacın zirvələrinin rənglərini oxuyuruq.

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

Zirvə -dən başlayan dərinlik axtarışına başlayırıq.

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

Nəticəni çıxarırıq.

print(*res[1:])