Problema Iepuri – OJI 2008, Clasa a 11-a
Rezumat
Se dau un arbore cu noduri și un număr Fiecărui nod din arbore i se asociază un număr natural cuprins între și Orice nod diferit de rădăcină trebuie să aibă un număr strict mai mare decât tatăl său. Să se determine numărul de moduri în care pot fi alese aceste numere, modulo
Soluție
Problema Iepuri este o problemă elementară de programare dinamică pe arbore. Dar nu cred că trebuie să fiți familiarizați cu acest concept pentru a înțelege soluția. În continuare, prin mă voi referi la valoarea asociată nodului Ideea este să notăm cu numărul de moduri în care putem asocia numere cuprinse între și subarborelui cu rădăcina în nodul astfel încât Dacă avem fixat atunci trebuie ca pentru orice fiu al lui De aici obținem recurența următoare:
Care se traduce în: este egal cu produsul valorilor unde este fiu al lui și Matricea poate fi calculată ușor în cadrul unui DFS din rădăcina arborelui. Când ne aflăm în nodul continuăm recursiv DFS-ul în fiii lui pentru a obține valorile După ce le avem pe acestea, putem folosi recurența de mai sus pentru a calcula
Deocamdată avem complexitatea deoarece, atunci când calculăm valoarea parcurgem stări pentru fiecare fiu adică stările acelea cu Putem reduce partea asta la calculând niște sume parțiale pe parcurs. Mai exact, la finalul funcției DFS, calculăm șirul unde Noua recurență reflectă scăderea complexității la
Sursă C++
Un detaliu de implementare interesant este modul în care am calculat rădăcina arborelui. Având în vedere că muchiile sunt date sub forma tată-fiu, rădăcina este singurul nod care nu apare ca fiu în lista muchiilor. Prin urmare, o putem calcula drept suma numerelor naturale mai mici sau egale cu adică minus suma fiilor din lista de muchii.
Alt detaliu important este că, pentru un nod aflat la adâncimea (începând de la nu poate fi mai mic decât din cauza condiției Din acest motiv, atunci când inițializez cu valorile pe îl iau doar de la la
Nu în ultimul rând, puteam calcula șirul direct peste vectorul dar pentru a păstra claritatea n-am mai făcut asta.
#include <bits/stdc++.h>using namespace std;
ifstream fin("iepuri.in");ofstream fout("iepuri.out");
const int MOD = 30011;
int main() { int n, k; fin >> n >> k; int root = n * (n + 1) / 2; vector<vector<int>> ad(n + 1); for (int i = 1; i < n; i++) { int x, y; fin >> x >> y; ad[x].push_back(y); root -= y; }
vector dp(n + 1, vector<int>(k + 1)); vector ps(n + 1, vector<int>(k + 2)); function<void(int, int)> dfs = [&](int node, int dpth) { for (int carr = dpth; carr <= k; carr++) dp[node][carr] = 1; for (int son : ad[node]) { dfs(son, dpth + 1); for (int carr = dpth; carr <= k; carr++) dp[node][carr] = dp[node][carr] * ps[son][carr + 1] % MOD; } ps[node][k] = dp[node][k]; for (int carr = k - 1; carr >= 1; carr--) ps[node][carr] = (dp[node][carr] + ps[node][carr + 1]) % MOD; }; dfs(root, 1); fout << ps[root][1] << '\n'; return 0;}
Dacă ai vreo nedumerire cu privire la problema Iepuri, lasă un comentariu și te voi ajuta