Zadanie

Bol raz jeden jednobunkový organizmus menom Cecil1. Bolo mu smutno, lebo nebolo nikoho, s kým by sa mohol porozprávať pri jedle, a tak sa Cecil začal deliť, aby mal koho pozvať na svoje párty.

Časom mal Cecil dosť potomkov, a tak sa rozhodol zorganizovať párty. Pozval na ňu všetkých svojich \(k\)-potomkov. (\(1\)-potomok Cecila je každé jeho dieťa, \(2\)-potomok je každé jeho vnúča, atď.)

Postupom času začali aj potomkovia Cecila organizovať párty podobným spôsobom. Každému svojmu \(k\)-potomkovi pošlú pozvánku na svoju párty. Tá má nasledovnú formu: “Na jeho veľkú párty ťa pozýva tvoj \(k\)-ty predok, sám magnificentný…”

Cecilovi potomkovia sú ale inteligentní, lebo to bolo v Cecilovych génoch. Je im jasné, že množstvo jedla a kofoly, ktoré sa im na párty ujde, je určené počtom účastníkov párty. Nie vždy sa teda oplatí ísť na párty. Pomôžte im pri rozhodovaní – pre každú pozvánku určite, koľko účastníkov bude na párty ak sa jej všetci pozvaní zúčastnia.

Úloha

Zadaný je zakorenený strom s \(n\) vrcholmi. Prichádza vám postupne \(m\) otázok. Každá otázka je určená vrcholom \(v\) a poradovým číslom predka \(k\). Pre každú otázku zistite, koľko \(k\)-potomkov má \(k\)-predok vrchola \(v\). Ak \(k\)-predok vrcholu \(v\) neexistuje, tak je odpoveď \(0\).

\(1\)-potomok vrcholu \(v\) je ľubovoľné jeho dieťa. Pre \(k > 1\) je \(k\)-potomok vrcholu \(v\) ľubovoľné dieťa niektorého \((k-1)\)-potomka \(v\). \(1\)-predok vrcholu \(v\) je otec \(v\) ak existuje. Pre \(k > 1\) je \(k\)-predok vrcholu \(v\) otec \((k-1)\)-predka \(v\).

Úlohu je nutné riešiť online – váš program sa nedozvie ďalšiu otázku, kým neodpovie na tú aktuálnu. Za každou odpoveďou preto použite fflush(stdio) alebo cout << flush, aby sa výstup vášho programu hneď odoslal testovaču.

Formát vstupu

Na prvom riadku vstupu je celé číslo \(t\) (\(1 \leq t \leq 1\,000\)) – počet testov. Každý z testov má nasledovný formát:

Prvý riadok testu obsahuje jedno celé číslo \(n\) (\(1 \leq n \leq 100\,000\)) – počet vrcholov stromu. Druhý riadok testu obsahuje \(n\) čísel \(p_1, p_2, \ldots, p_n\)\(i\)-te z nich je číslo priameho predka vrcholu \(i\). Ak je vrchol \(i\) koreňom stromu, tak \(p_i = 0\). (Pre \(i\) také, že vrchol \(i\) nie je koreňom stromu platí \(1 \leq p_i \leq n\).) Môžete predpokladať, že zadaný graf je naozaj strom.

Tretí riadok testu obsahuje jedno celé číslo \(m\) (\(1 \leq m \leq 100\,000\)) – počet otázok. Nasleduje \(m\) riadkov, \(i\)-ty z nich obsahuje dve celé čísla \(v_i, k_i\) (\(1 \leq v_i, k_i \leq n\)) popisujúce otázku.

Súčet \(n\) zo všetkých testov nepresiahne \(100\,000\), a súčet \(m\) zo všetkých testov nepresiahne \(150\,000\).

Formát výstupu

Pre každú otázku vypíšte na samostatný riadok jedno celé číslo – počet \(k\)-potomkov \(k\)-predka vrcholu \(v\).

Príklad

Input:

2
8
4 4 2 0 4 5 5 5
6
1 1 // 1-predok 1 je 4.
    // Jeho 1-potomkovia su 5, 2 a 1.
1 2 // 2-predok 1 neexistuje.
3 1 // 1-predok 3 je 2.
    // Jeho 1-potomok je len 3.
3 2 // 2-predok 3 je 4.
    // Jeho 2-potomkovia su 8, 7, 6 a 3.
3 3 // 3-predok 3 neexistuje.
5 1 // 1-predok 5 je 4.
    // Jeho 1-potomkovia su 5, 2 a 1.
5
3 0 2 5 1
5
4 5
4 1
1 2
1 3
2 1

Output:

3
0
1
4
0
3
0
1
1
0
0

Text uvedený za // je komentár – v skutočných vstupoch sa žiadne komentáre samozrejme nachádzať nebudú. Druhý test má správny formát. Strom v prvom teste vyzerá nasledovne:


  1. Možno ste už počuli o jeho pradedovi Bacilovi.

Povedzme, že chceme zodpovedať otázku určenú vrcholom \(v\) a číslom \(k\) – teda chceme nájsť počet \(k\)-potomkov \(k\)-predka vrcholu \(v\). To možno rozdeliť na dve podotázky:

  • Zadaný je vrchol \(v_1\) a číslo \(k_1\). Existuje \(k_1\)-predok vrcholu \(v_1\)? Ak áno, ktorý vrchol to je?

  • Zadaný je vrchol \(v_2\) a číslo \(k_2\). Koľko \(k_2\)-potomkov má vrchol \(v_2\)?

Pomalé riešenie

Na zodpovedanie prvej podotázky môžeme postupovať nasledovne:

  • Ak \(k = 0\), tak \(k\)-predok vrcholu \(v\) je samotný vrchol \(v\).

  • V opačnom prípade je to ten istý vrchol, ako \((k-1)\)-predok otca \(v\) – a toho vieme nájsť rekurzívne.

Zistiť odpoveď pre druhú podotázku vieme takto:

  • Ak \(k = 0\), tak je odpoveď zrejme \(1\) – jediný \(0\)-potomok vrcholu \(v\) je samotný vrchol \(v\).

  • V opačnom prípade nech \(d_1, d_2, \ldots, d_i\) sú všetky deti vrcholu \(v\). Označme \(r_1, r_2, \ldots, r_i\) postupne počty \((k-1)\)-potomkov týchto vrcholov. Ľahko si možno rozmyslieť, že \(v\) má práve \(r_1 + r_2 + \ldots + r_i\) \(k\)-potomkov. No a hodnoty \(r_1, r_2, \ldots, r_i\) vieme vypočítať rekurzívne.

Prvá fáza algoritmu môže mať až toľko krokov, koľko je hĺbka vrcholu \(v\), keďže postupne zisťujeme jeho \(1\)-predka, \(2\)-predka, … A to môže byť až lineárne od \(n\).

Druhá fáza algoritmu môže mať až toľko krokov, koľko je počet vrcholov v podstrome vrcholu \(v\) – a tých môže byť až lineárne veľa od \(n\).

Časová zložitosť na zodpovedanie jednej otázky je teda \(O(n)\), celkovo máme preto časovú zložitosť \(O(nq)\). Pamäťová zložitosť je \(O(n)\) – pamätáme si iba reprezentáciu stromu.

#include <iostream>
#include <vector>
using namespace std;

struct Problem {
  
  int n, koren; // pocet vrcholov, cislo korenoveho vrchola
  vector<int> otec; // otec[i] je rodic vrchola i
  vector<vector<int> > synovia; // synovia[i] je zoznam potomkov vrchola i

  // nacitame pocet vrcholov, strom
  void nacitaj () {
    cin >> n;
    otec.resize(n);
    synovia.resize(n);
    for (int i = 0; i < n; i++) {
      cin >> otec[i];
      otec[i]--;
      if (otec[i] == -1) {
        koren = i;
      }
      else {
        synovia[otec[i]].push_back(i);
      }
    }
  }

  // vrati k-predka vrcholu v
  int predok (int v, int k) {
    if (k == 0) {
      return v;
    }
    if (v == koren) {
      return -1;
    }
    return predok(otec[v], k - 1);
  }

  // vrati pocet k-potomkov vrcholu v
  int prehladaj (int v, int k) {
    if (k == 0) {
      return 1;
    }
    int vysledok = 0;
    for (int i = 0; i < (int) synovia[v].size(); i++) {
      int syn = synovia[v][i];
      vysledok += prehladaj(syn, k - 1);
    }
    return vysledok;
  }

  // vrati odpoved na otazku "kolko k-potomkov ma k-predok vrcholu v?"
  int zodpovedaj (int v, int k) {
    int kPredok = predok(v, k);
    if (kPredok == -1) {
      return 0;
    }
    int vysledok = prehladaj(kPredok, k);
    return vysledok;
  }

  // odpovieme na vsetky otazky
  void zodpovedajVsetko () {
    int q; // pocet otazok
    cin >> q;
    for (int otazka = 0; otazka < q; otazka++) {
      int v, k;
      cin >> v >> k;
      v--;
      int vysledok = zodpovedaj(v, k);
      cout << vysledok << "\n";
    }
  }

  // spravi vsetko -- nacita strom a odpovie na otazky
  void spravVsetko () {
    nacitaj();
    zodpovedajVsetko();
  }
  
};

int main () {
  int t;
  cin >> t;
  for (int test = 0; test < t; test++) {
    Problem p;
    p.spravVsetko();
  }
  return 0;
}

Rýchlejšie zodpovedanie prvej podotázky

Predstavme si, že by sme si nepamätali pre každý vrchol iba jeho \(1\)-predka, ale aj jeho \(2\)-predka. Potom by sme \(k\)-predka vrcholu \(v\) vedeli nájsť zhruba dvakrát rýchlejšie:

  • Ak \(k \geq 2\), tak sa rekurzívne zavoláme – zrejme hľadáme \((k - 2)\)-predka vrcholu \(x\), ktorý je \(2\)-predkom vrcholu \(v\).

  • Ak \(k = 1\), tak odpoveďou je \(1\)-predok vrcholu \(v\).

  • V poslednom prípade \(k = 0\), a odpoveďou je samotný vrchol \(v\).

Ďalej si predstavme, že si nepamätáme iba \(2\)-predkov, ale aj ďalších predkov s takými číslami, ktoré sú mocninami dvojky – \(4, 8, 16, \ldots\). Takto si pre každý vrchol pamätáme rádovo \(\log{n}\) čísel. Žiadny vrchol totiž nemá viac ako \(n\) predkov, a nemá preto zmysel počítať jeho \((2^i)\)-predka pre \(2^i > n\).

Zodpovedanie otázky má veľmi podobnú formu.

  • Ak \(k > 0\), nájdeme najväčšie číslo tvaru \(2^i\), ktoré je menšie ako \(k\). Ak vrchol \(v\) nemá \((2^i)\)-predka, tak nemôže mať ani \(k\)-predka. V opačnom prípade vieme v konštantnom čase pozrieť, ktorý vrchol je \((2^i)\)-predok \(v\). Rekurzívne nájdeme \((k - 2^i)\)-predka tohto vrcholu.

  • Ak \(k = 0\), tak odpoveď je zrejme \(v\).

Navyše si všimnime, že ak v rekurzívnom volaní skočíme z \(v\) na jeho \((2^i)\)-predka, potom v nasledujúcom rekurzívnom volaní určite môžeme skočiť najviac na \((2^{i - 1})\)-predka.

Ak by sme totiž znovu skočili hore aspoň o \(2^i\), skončili by sme vo vrchole, ktorý je aspoň \((2^i + 2^i)\)-predkom \(v\) – potom určite \(2^{i + 1} \leq k\). Ale to by sme vedeli skočiť o \(2^{i + 1}\) už v prvom volaní.

Takže, ak označíme \(i\) najväčšie číslo také, že \(v\)\((2^i)\)-predka, stačí nám pri hľadaní \(k\)-predka skúšať \(2^i, 2^{i-1}, 2^{i-2}, \ldots, 1\). Každé raz a v tomto poradí. Dostávame tak algoritmus, ktorý má časovú zložitosť \(O(\log{n})\).

int n; // pocet vrcholov stromu
vector<vector<int> > predok; // predok[i][j] je (2^j)-predok vrcholu i
vector<int> hlbka; // hlbka[i] je hlbka vrcholu i

// vrati k-predka vrcholu v alebo -1 ak neexistuje
int kPredok (int v, int k) {
  if (k > hlbka[v]) { // taky daleky predok neexistuje
    return -1;
  }
  int kolkyPredok = 1;
  for (int i = 1; i < (int) predok[v].size(); i++) {
    kolkyPredok *= 2;
  }
  for (int i = (int) predok[v].size() - 1; i >= 0; i--) {
    if (k >= kolkyPredok) {
      k -= kolkyPredok;
      v = predok[v][i];
    }
    kolkyPredok /= 2;
  }
  return v;
}

Ešte sme ale vôbec neriešili, ako rýchlo vypočítať týchto mocninových predkov. Predstavme si, že chceme nájsť \((2^i)\)-predka vrcholu \(v\). To ale nie je nič iné, ako jeho \((2^{i - 1} + 2^{i - 1})\)-predok. Takže môžeme postupovať tak, že najprv nájdeme jeho \((2^{i - 1})\)-predka – nech to je \(x\). Potom nájdeme \((2^{i - 1})\)-predka vrcholu \(x\) – výsledný vrchol je práve \((2^i)\)-predok vrcholu \(v\).

Na začiatku pre každý vrchol vypočítame jeho \(1\)-predka (napríklad prehľadávaním do hĺbky). Potom vieme pre každý vrchol vypočítať jeho \(2\)-predka, potom pre každý vrchol jeho \(4\)-predka, potom pre každý vrchol jeho \(8\)-predka, a tak ďalej.

Časová zložitosť tohto predpočítania je teda \(O(n\log{n})\), a pamäťová takisto, keďže si všetkých týchto predkov musíme pamätať.

int n; // pocet vrcholov stromu
vector<vector<int> > predok; // predok[i][j] je (2^j)-predok vrcholu i
                            // na zaciatku predok[i] obsahuje jedine otca vrcholu i

// pre kazdy vrchol spocitame jeho 2^i predkov
void predratajPredkov () {
  bool este = true;
  int i = 0;
  while (este) {
    este = false;
    for (int v = 0; v < n; v++) {
      if ((int) predok[v].size() <= i) { // 2^i predok vrcholu v neexistuje
        continue;
      }
      int medziPredok = predok[v][i];
      if ((int) predok[medziPredok].size() <= i) { // 2^i predok medziPredka neexistuje
        continue;
      }
      int koncovyPredok = predok[medziPredok][i];
      predok[v].push_back(koncovyPredok);
      este = true; // ma zmysel pokracovat jedine vtedy, ked sme pre nejaky vrchol
                  // nasli jeho 2^i predka
    }
    i++;
  }
}

Rýchlejšie zodpovedanie druhej podotázky

Rozdeľme si všetky vrcholy do vrstiev podľa toho, akú majú vzdialenosť od koreňa stromu. Na prvej vrstve sa nachádza iba koreň, na druhej sú jeho deti, na tretej sú zas ich deti, … Pre každý vrchol \(v\) označme \(h(v)\) číslo vrstvy, na ktorej sa nachádza.

Zrejme \(k\)-potomkov vrcholu \(v\) stačí hľadať medzi vrcholmi na vrstve \(h(v) + k\). Tých ale stále môže byť rádovo \(n\).

Po chvíli kreslenia stromov a pozorovania rôznych \(k\)-potomkov rôznych vrcholov \(v\) odpozorujeme, že tí potomkovia akosi vždy tvoria vo vrstve súvislý úsek. V nasledujúcom obrázku sú napríklad zobrazení \(2\)-potomkovia vrcholu \(5\):

Ten istý strom môže ale vyzerať úplne inak:

Uvedomíme si, čo určuje poradie vrcholov vo vrstve – pre každý vrchol máme totiž zoznam jeho synov, ale týchto synov môžeme zľava doprava nakresliť v ľubovoľnom poradí. Zvoľme si teda pre každý vrchol \(v\) nejaké poradie jeho synov.

Nahliadneme, že ak \(a, b\) sú synovia vrcholu \(v\) a \(a\) je pred \(b\), tak v rámci každej vrstvy je každý potomok \(a\) v tej vrstve pred každým potomkom \(b\).

Na základe týchto pozorovaní vieme poradie vo vrstvách zostrojiť jednoduchým prehľadávaním do hĺbky. Keď prvýkrát navštívime (ďalej objavíme) vrchol \(v\), tak si pre neho zapamätáme, koľký v poradí objavený vrchol to je. Potom postupne navštívime jeho synov v poradí, ktoré sme si zvolili. V rámci každej vrstvy sú potom vrcholy usporiadané podľa toho, kedy sme ich objavili.

A keď už vieme, že vo vrstve budú vrcholy usporiadané podľa času ich objavenia, tak ich do vrstvy vieme pridať hneď keď ich objavíme.

vector<int> zac, kon; // zac[i] (kon[i]) je zaciatocne (koncove) cislo vrchola i

vector<vector<int> > synovia; // synovia[i] je zoznam potomkov vrchola i
vector<vector<int> > vrstvy; // vrstvy[i] je zoznam vrcholov, ktore su vzdialene od korena presne i

// spracovanie podstromu vrcholu v, ktory sedi na vrstve s cislom h
void dfs (int v, int h) {
  zac[v] = cas;
  cas++;
  
  if ((int) vrstvy.size() <= h) {
    vrstvy.push_back(vector<int>());
  }
  vrstvy[h].push_back(v);
  
  for (int i = 0; i < (int) synovia[v].size(); i++) {
    int syn = synovia[v][i];
    dfs(syn, h + 1);
  }

  kon[v] = cas;
  cas++;
}

Teraz už máme vrcholy v každej vrstve usporiadané tak, že keď hľadáme \(k\)-potomkov niektorého vrcholu \(v\), tvoria vo vrstve súvislý úsek. Ako ale zistíme, kde ten úsek začína a kde končí?

Zoberme si ľubovoľnú vrstvu, a nech \(x\) je niektorý vrchol na nej. Rozmyslite si, že ak \(x\) je potomkom \(v\), tak sme ho určite objavili neskôr ako sme objavili \(v\).

Pozrime sa na úsek potomkov \(v\) na tejto vrstve. Prvý vrchol \(x_1\) v tomto úseku sme objavili neskôr, ako \(v\) – a spomedzi všetkých takých vrcholov sme ho objavili najskôr. Vieme ho teda vo vrstve binárne vyhľadať, nakoľko vrcholy na nej sú usporiadané podľa času ich objavenia.

Takto sme našli začiatok úseku, ako ale nájdeme jeho koniec? Podobne – pre každý vrchol si tiež zistíme, kedy sme ho naposledy navštívili (ďalej opustili).

Opäť si zoberme ľubovoľnú vrstvu, a nech \(x\) je niektorý vrchol na nej. Potom ak \(x\) je potomkom \(v\), tak sme ho určite opustili skôr, ako sme opustili \(v\).

Rozmyslite si, že v každej vrstve je usporiadanie vrcholov podľa času prvej návštevy rovnaké, ako usporiadanie podľa času poslednej návštevy.

Pozrime sa teda na úsek potomkov \(v\) na tejto vrstve. Posledný vrchol \(x_2\) v tomto úseku bol opustený skôr, ako sme opustili \(v\) – a spomedzi všetkých takých vrcholov sme ho opustili najneskôr. Vieme ho teda vo vrstve binárne vyhľadať.

No a ak prvý vrchol v úseku je \(i\)-ty, a posledný vrchol v úseku je \(j\)-ty, počet vrcholov vo vnútri úseku je \(j - i + 1\).

Rekapitulácia

Zrekapitulujme si naše riešenie. Najprv v čase \(O(n\log{n})\) predpočítame pre každý vrchol jeho \(1, 2, 4, 8, \ldots\) predkov. V čase \(O(n)\) prehľadáme graf do hĺbky, a pre každý vrchol zistíme čas jeho prvej návštevy a čas poslednej návštevy. Pritom zostrojujeme zoznamy vrcholov v každej vrstve. Celkovo máme čas \(O(n\log{n})\) na predpočítanie.

Keď odpovedáme na otázku určenú vrcholom \(v\) a číslom \(k\), v čase \(O(\log{n})\) nájdeme \(k\)-predka vrcholu \(v\). Potom vo vrstve \(h(v)\) binárne vyhľadáme začiatok úseku potomkov \(k\)-predka \(v\), a tiež koniec tohto úseku – to nám tiež zaberie čas \(O(\log{n})\). Na otázkach teda minieme \(O(q\log{n})\) času.

Celková časová zložitosť je potom \(O((n + q) \log{n})\). Pamäťová zložitosť je \(O(n\log{n})\).

Rýchlejšie a úspornejšie

Vráťme sa späť k prvej podotázke. V nej sme dostali zadaný vrchol \(v\) a číslo \(k\), a chceli sme vedieť, či existuje \(k\)-predok vrcholu \(v\) a ak áno, ktorý vrchol to je. Označme ho \(x\).

Teraz už ale pre každý vrchol vieme, kedy sme ho prvýkrát navštívili, a kedy sme ho naposledy navštívili. A tiež vieme povedať, v ktorej vrstve sa nachádza. To využijeme na vytvorenie lepšieho riešenia.

Vieme totiž, že \(x\) musí spĺňať nasledovné:

  • Musí ležať na vrstve s číslom \(h(v) - k\).

  • Objavili sme ho skôr, ako vrchol \(v\).

  • Opustili sme ho neskôr, ako \(v\).

Potom vieme \(x\) vo vrstve binárne vyhľadať.

Týmto sme si ušetrili \(n\log{n}\) operácii potrebných na predpočítanie, a s tým spojených \(n\log{n}\) pamäte. Dostávame tak celkovú časovú zložitosť \(O(n + q\log{n})\), a pamäťovú zložitosť \(O(n)\).

#include <iostream>
#include <vector>
using namespace std;

struct Problem {
  
  int n, koren; // pocet vrcholov, cislo korenoveho vrchola
  vector<int> otec; // otec[i] je rodic vrchola i
  vector<vector<int> > synovia; // synovia[i] je zoznam potomkov vrchola i

  vector<int> zac, kon; // zac[i] (kon[i]) je zaciatocne (koncove) cislo vrchola i
  vector<vector<int> > vrstvy; // vrstvy[i] je zoznam vrcholov, ktore su vzdialene od korena presne i
  vector<int> hlbka; // hlbka[i] je hlbka vrcholu i

  // nacitame pocet vrcholov, strom a inicializujeme zac, kon
  void nacitaj () {
    cin >> n;
    otec.resize(n);
    synovia.resize(n);
    zac.resize(n);
    kon.resize(n);
    hlbka.resize(n);
    for (int i = 0; i < n; i++) {
      cin >> otec[i];
      otec[i]--;
      if (otec[i] == -1) {
        koren = i;
      }
      else {
        synovia[otec[i]].push_back(i);
      }
    }
  }

  // tymto prehladame graf do hlbky a spocitame zac, kon, vrstvy, hlbka
  void dfs (int v, int h, int& prveVolne) {
    zac[v] = prveVolne;
    prveVolne++;
    hlbka[v] = h;

    if ((int) vrstvy.size() <= h) {
      vrstvy.push_back(vector<int>());
    }
    vrstvy[h].push_back(v);

    for (int i = 0; i < (int) synovia[v].size(); i++) {
      int syn = synovia[v][i];
      dfs(syn, h + 1, prveVolne);
    }
    kon[v] = prveVolne;
    prveVolne++;
  }

  // tymto spocitame cele zac, kon, vrstvy
  void dfs () {
    int prveVolne = 0;
    dfs(koren, 0, prveVolne);
  }

  // vrati najvacsie i take, ze zac[vrstva[i]] < limit
  int binarneVyhladaj (int limit, vector<int>& vrstva) {
    int l = -1;
    int r = (int) vrstva.size();
    while (r - l > 1) {
      int s = (l + r) / 2;
      if (zac[vrstva[s]] < limit) {
        l = s;
      }
      else {
        r = s;
      }
    }
    return l;
  }

  // vrati k-predka vrchola v
  int predok (int v, int k) {
    int h = hlbka[v] - k;
    if (h < 0) {
      return -1;
    }
    int i = binarneVyhladaj(zac[v], vrstvy[h]);
    return vrstvy[h][i];
  }

  // vrati pocet k-potomkov vrchola v
  int kPotomkov (int v, int k) {
    int h = hlbka[v] + k;
    if (h >= (int) vrstvy.size()) {
      return 0;
    }
    int prvy = binarneVyhladaj(zac[v], vrstvy[h]) + 1;
    int posledny = binarneVyhladaj(kon[v], vrstvy[h]);
    return posledny - prvy + 1;
  }

  // vrati odpoved na otazku "kolko k-potomkov ma k-predok vrcholu v?"
  int zodpovedaj (int v, int k) {
    int kPredok = predok(v, k);
    if (kPredok == -1) {
      return 0;
    }
    int vysledok = kPotomkov(kPredok, k);
    return vysledok;
  }

  // odpovieme na vsetky otazky
  void zodpovedajVsetko () {
    int q; // pocet otazok
    cin >> q;
    for (int otazka = 0; otazka < q; otazka++) {
      int v, k;
      cin >> v >> k;
      v--;
      int vysledok = zodpovedaj(v, k);
      cout << vysledok << "\n";
    }
  }

  // spravi vsetko -- nacita strom a odpovie na otazky
  void spravVsetko () {
    nacitaj();
    dfs();
    zodpovedajVsetko();
  }
  
};

int main () {
  int t;
  cin >> t;
  for (int test = 0; test < t; test++) {
    Problem p;
    p.spravVsetko();
  }
  return 0;
}

Diskusia

Tu môžte voľne diskutovať o riešení, deliť sa o svoje kusy kódu a podobne.

Pre pridávanie komentárov sa musíš prihlásiť.