Sunteți pe pagina 1din 10

OJI 2019

#2971 tairos
Se dă un arbore cu N noduri, numerotate de la 1 la N. Arborele se va transforma astfel: la
oricare etapă fiecare nod de gradul 1 diferit de rădăcină din arborele actual se înlocuiește
cu un arbore identic cu cel dat inițial, iar la următoarea etapă procedeul se va relua pentru
arborele obținut, formându-se astfel un arbore infinit. În următoarele trei imagini se prezintă
un exemplu de arbore dat inițial, arborele obținut după prima etapă de prelungire a
frunzelor și arborele obținut după două etape de prelungire a frunzelor.

Să se determine câte noduri se află la distanță D de rădăcina arborelui infinit.


Pe prima linie a fișierului de intrare tairos.in se va afla un număr natural N, reprezentând
numărul de noduri din arborele dat inițial. Pe a doua linie se va afla numărul întreg D, cu
semnificația de mai sus, iar fiecare dintre următoarele N-1 linii conține câte două numere
întregi x și y cu semnificația că în arborele dat inițial există muchia [x, y].
Fișierul de ieșire tairos.out va conține un singur număr, și anume restul împărțirii
numărului de noduri cerut la numărul 1.000.000.007.

 2 ≤ N ≤ 100
 1 ≤ D ≤ 10.000
 Un arbore este un graf neorientat, conex și fără cicluri.
 Distanța dintre două noduri x și y ale unui arbore este egală cu numărul de muchii
ale unui lanț cu extremitățile în nodurile x și y, lanț format din noduri distincte.
 Rădăcina va fi considerată ca fiind nodul 1;
 Pentru teste în valoare de 17 puncte avem N = 3
 Pentru teste în valoare de alte 22 puncte răspunsul este ≤ 10 000;
 În concurs s-au acordat 10 puncte din oficiu. Aici se acordă pentru exemplele din
enunț.
tairos.in

4
3
1 2
3 1
3 4

tairos.out

Arborele dat în fișierul de intrare are 4 noduri. Se cere numărul nodurilor aflate la


distanța 3 față de rădăcină.
Urmărind imaginile din exemplele de mai sus,la distanța 3 avem
următoarele 5 noduri: 222, 223, 241, 421 și 43.
tairos.in

5
3
1 2
3 1
3 5
4 3

tairos.out

tairos.in

5
25
2 1
2 3
1 4
5 2

tairos.out

33554432

Problema tairos – descrierea soluției


Subtask-ul cu N=3 în valoare de 17 puncte
Pentru N = 3 structura arborelui inițial poate fi:

 Avem cele 2 muchii legate de nodul 1 (muchiile 1-2 și 1-3). Numărul de noduri la


distanța D de rădăcina va fi întotdeauna 2D, din modul de expandare al arborilor.
 Avem cele 2 muchii legate în lanț: 1-2 și 2-3 (ori, similar 1-3 și 3-2). Numărul de
noduri la distanța D de rădăcină va fi întotdeauna 1, deoarece permanent lanțul doar
se va lungi în jos.

Simularea efectiva a extinderii arborelui, soluție parțială în


valoare de  26-30 puncte
O soluție de punctaj parțial ar fi să facem un DFS, unde să reținem permanent adâncimea
la care se află nodul procesat. Astfel, atunci când suntem într-o frunză la adâncimea H, să
continuăm DFS-ul cu nodul 1 și adâncimea H. Prin această procedură simulăm prelungirea
arborilor doar atunci când avem nevoie. Vom da return din DFS doar atunci când
adâncimea actuala este egală cu adâncimea din cerință D, moment în care vom și
incrementa rezultatul final.
Putem observa ca numărul total de noduri parcurse de aceasta abordare este T[1] +
T[2] + T[3] + .. + T[D], unde T[i] reprezintă numărul de noduri la distanta i de
rădăcina 1. (practic, răspunsul la cerința ar fi T[D]). Grosolan putem aproxima
complexitatea la O(raspuns^2) și de aceasta soluția va obține punctajul doar pentru
subtask-ul raspuns ≤ 10 00 și puțin din subtask-ul N=3, mai exact cazul lanț.

Soluția de 90 de puncte
Soluția problemei constă în numărarea frunzelor în arborele inițial.
Fie FR[i] = numărul de frunze la distanța i de rădăcina 1 în arborele inițial
Fie NT[i] = numărul de noduri la distanța i de rădăcina 1 în arborele inițial care nu sunt
frunze.
Știm ca arborele inițial are doar 100 de noduri, deci acești doi vectori de mai sus FR și NT nu
vor fi deloc mari. În continuare vom defini V[i] ca fiind numărul de noduri la distanța i care
au fost cândva frunze în procesul de extindere al arborelui inițial.
Inițial V[0] = 1 și V[i] = 0 oricare i != 0 (caz particular de pornire. Dacă extindem un
nod izolat folosind tehnica din enunț vom obține exact arborele inițial).
Putem parcurge de la 0 la D vectorul V pentru a genera frunze la dreapta în vector folosind
tehnica programării dinamice. Mai exact, o recurență înainte de forma:
fiind la poziția i și având deja calculată valoarea V[i],
V[i + 1] += V[i] * FR[1]
V[i + 2] += V[i] * FR[2]
…..
V[i + adancime_arbore_initial] += V[i] * FR[adancime_arbore_initial]
În cuvinte, dacă la distanta i avem V[i] noduri și știm ca în arborele inițial
aveam FR[k] frunze la distanța k de rădăcina 1, atunci prelungind toate cele V[i] noduri
aflate la distanța i, vom obține V[i] * FR[k] noi frunze la distanța V[i + k].
Acum, pentru a afla exact câte noduri sunt la distanța D de rădăcina 1, trebuie sa nu uitam
de nodurile din arborele inițial care nu sunt frunze, cele reținute în vectorul NT.
Răspunsul este V[D] + V[D – 1] * NT[1] + V[D – 2] * NT[2] + ... + V[D -
a_a_i] * NT[a_a_i], unde a_a_i este adâncimea arborelui inițial.
Clarificare pentru formula de mai sus:
V[D] reprezintă numărul de noduri foste frunze aflate la distanța D de rădăcina (acestea
clar trebuie numărate).
V[D – 1] * NT[1] reprezintă nodurile-frunze aflate la distanța D-1 de rădăcină, pe care le
prelungim pentru ultima dată. Pentru fiecare arbore astfel prelungit trebuie sa număram
câte noduri ne-frunze (deoarece frunzele au fost deja numărate) are la distanța 1 –
informație reținută în NT[1].
V[D – k] * NT[k] exact ca mai sus.

Complexitate timp finală O(MAX_D * MAX_N), adică D * adancime_arbore_initial. Aveți


grija permanent sa nu uitați de modulo.
Solutie oficiala

#include <iostream>

#include <fstream>

using namespace std;

ifstream fin("tairos.in");

ofstream fout("tairos.out");

const long long R=1000000007;

int N, D, mat[105][105], tata[105], lungime[105],sterile[105],


frunze[105],nrfrunze[100],distanta[105], nfr, maxadanc;

bool viz[105], steril[105]; /// un nod este steril daca nu e frunza

long long nrsol[10005];

void dfs(int nod, int adancime, int &maxadanc)

if (maxadanc<adancime) maxadanc=adancime; /// calculam adancimea maxima al arborelui

lungime[nod]=adancime;

for (int i=1;i<=mat[nod][0];i++)

if(!viz[mat[nod][i]]) /// daca un nod nu a fost vizitat, atunci este fiul nodului
curent

steril[nod]=true; /// nodul curent nu poate fi frunza daca are un


descendent

viz[mat[nod][i]]=true;

tata[mat[nod][i]]=nod;

dfs(mat[nod][i], adancime+1,maxadanc);

void citire(int &N, int &D, int tata[105])

int x,y;

fin>>N>>D;

for(int i=1; i<=N-1; i++)


{

fin>>x>>y;

mat[x][0]++;mat[x][mat[x][0]]=y; /// lista de adiacenta

mat[y][0]++;mat[y][mat[y][0]]=x;

viz[1]=true; /// se stie ca nodul 1 este radacina

dfs(1,0,maxadanc); /// dfs porneste cu nivel 0, radacina 1 si va calcula


adancimea maxima a arborelui

int main()

citire(N,D,tata);

for(int i=1;i<=N; i++)

if (steril[i])

sterile[lungime[i]]++; /// creste numarul nodurilor sterile de o anumita distanta

else

frunze[lungime[i]]++; /// creste numarul frunzelor de o anumita distanta

nfr=0;

for(int i=1;i<=N;i++)

if(frunze[i]) /// daca exista frunze la distanta i de radacina

nfr++;
nrfrunze[nfr]=frunze[i]; /// numarul frunzelor la

distanta[nfr]=i; /// distanta data

nrsol[0]=1;

for (int i=1;i<=maxadanc;i++)

for (int j=1; j<=nfr; j++)

if (distanta[j]<=i)

nrsol[i]=(nrsol[i]+(nrsol[i-distanta[j]]*nrfrunze[j])%R)%R; ///
construiesc valorile initiale pana la maxadanc

for (int i=maxadanc+1; i<=D; i++)

for (int j=1; j<=nfr; j++)

nrsol[i]=(nrsol[i]+(nrsol[i-distanta[j]]*nrfrunze[j])%R)%R; /// construiesc


valorile initiale pana la maxadanc

for (int i=1;i<=N;i++) {


if (D - i >= 0)

nrsol[D]=(nrsol[D]+(nrsol[D-i]*sterile[i])%R)%R; /// copletam cu nodurile sterile

fout<<nrsol[D];

return 0;

#2968 conexidad
Fie un graf neorientat cu N noduri și M muchii, care NU este conex.
Să i se adauge grafului un număr minim de muchii, astfel încât acesta să devină conex.
Fie extra  numărul de muchii nou-adăugate care sunt incidente cu nodul i, iar max_extra cea
i

mai mare dintre valorile extra , extra ,… , extra . Mulțimea de muchii adăugate trebuie să
1 2 N

respecte condiția ca valoarea max_extra să fie minimă.


Pe prima linie a fișierului de intrare conexidad.in se află două numere naturale N și M, iar pe
fiecare dintre următoarele M linii se află câte o pereche de numere a, b, semnificând faptul că
există muchia [a,b]. Numerele aflate pe aceeași linie a fișierului sunt separate prin câte un
spațiu.
Fișierul de ieșire conexidad.out va conține pe prima linie valoarea max_extra. Pe a doua
linie va conține valoarea K reprezentând numărul de muchii nou-adăugate în graf. Fiecare dintre
următoarele K linii va conține câte o pereche de numere c, d, separate prin câte un spațiu,
semnificând faptul că se adaugă grafului muchia [c,d].

 1 ≤ N ≤ 100
 0 ≤ M ≤ N*(N-1)/2
 Nodurile grafului sunt numerotate de la 1 la N inclusiv.
 Muchiile prezente în fișierul de intrare sunt distincte.
 Pentru orice muchie [a,b] aflată în fișierul de intrare, avem a ≠ b.
 Graful din fișierul de intrare nu este conex.
 În cazul în care soluția afișată pentru un anumit test conectează graful cu număr minim
de muchii, dar nu minimizează valoarea lui max_extra, se vor acorda 50% din punctajul
pentru testul respectiv.
 Dacă există mai multe soluții optime, se va admite oricare dintre acestea.
 În concurs s-au acordat 10 puncte din oficiu. Aici se acordă pentru exemplele din enunț.

conexidad.in

4 2
1 2
4 2
conexidad.out

1
1
3 1

Graful este format din două componente conexe, cu noduri din mulțimea {1, 2, 4} respectiv
nodul izolat 3. După adăugarea muchiei (3,1) vom avea
valorile extra =1, extra =0, extra =1, extra =0, deci max_extra = 1. Se poate demonstra
1 2 3 4

că nu există soluție cu max_extra < 1.


conexidad.in

5 1
3 4

conexidad.out

2
3
1 3
2 3
4 5

Graful este format din patru componente conexe, cu noduri din mulțimea {3, 4}, respectiv
nodurile izolate 1, 2 și 5. După adăugarea muchiilor (1,3), (2,3) și (4,5), vom avea
valorile extra =1, extra =1, extra =2, extra =1, extra =1, deci max_extra = 2. Se poate
1 2 3 4 5

demonstra că nu există soluție cu max_extra < 2.

Problema conexidad – descrierea soluției


- Numărul minim de muchii necesare pentru a conecta graful este C – 1, unde C este
numărul de componente conexe. Componentele conexe se pot determina printr-o
parcurgere în lățime sau în adâncime.
- Observăm că este posibil întotdeauna să conectăm componentele într-un lanț, selectând
câte un nod din fiecare componentă iar apoi legând aceste noduri secvențial.
- Această construcție produce o valoare max_extra cel mult egală cu 2. Valoarea
lui max_extra nu poate fi niciodată 0, deci în continuare rămâne să analizăm cazurile în
care este posibil să obținem max_extra = 1.
- Observăm că dacă toate componentele conexe au mărime cel puțin 2, este posibil să le
legăm în lanț cu valoarea max_extra = 1, folosind pentru fiecare componentă două
noduri diferite pentru a duce muchia către componenta precedentă, respectiv către
componenta următoare. Rămâne să analizăm cazul componentelor de mărime 1 (numite și
noduri izolate).
- La modul general, o soluție care urmărește max_extra = 1 nu va duce muchie între două
noduri izolate, fiindcă conectarea ulterioară a acestora cu restul grafului va adăuga sigur o
a doua muchie cel puțin unuia dintre noduri. Excepție face graful alcătuit din doar două
noduri, ambele izolate.
- În consecință, dorim să conectăm nodurile izolate cu componente mari (de mărime cel
puțin 2) prin noduri ale acestor componente care nu au fost încă folosite pentru a duce
muchii.
- Având B componente mari, exact 2 * (B – 1) noduri ale acestora vor fi folosite pentru a
conecta componentele mari între ele. Restul nodurilor sunt considerate libere și pot fi legate
cu noduri izolate. Dacă există suficiente noduri libere pentru a acoperi toate nodurile
izolate, max_extra = 1, altfel max_extra = 2.
- Soluția poate fi implementată în timp O(N + M), dar limitele datelor de intrare sunt
suficient de mici pentru a oferi punctaj maxim unor soluții de complexitate O(N ^
3) sau O(N ^ 2).

#include<iostream>

#include<fstream>

using namespace std;

ifstream fin("conexidad.in");

ofstream fout("conexidad.out");

int a[101][101],viz[101],ind,n,x,y,cnt,m,extra[101],laturi[200];

void dfs(int x0)

viz[x0]=cnt+1;

for(int j=1;j<=n;j++)

if(a[x0][j]&&!viz[j])

dfs(j);

void conex()

cnt=0;

int k=1,i;

while(k)

i=1;

while(viz[i]!=0&&i<=n)

i++;

if(i<=n)

dfs(i);

cnt++;

else

k=0;

}
}

int minExtraPos(int componenta)

int mn=100,pos=1;

for(int i=n;i>=1;i--)

if(extra[i]<=mn&&viz[i]==componenta)

mn=extra[i];

pos=i;

return pos;

int main()

fin>>n>>m;

for(int i=1;i<=m;i++)

fin>>x>>y;

a[x][y]=a[y][x]=1;

conex();

int cnt2=cnt;

for(int i=1;i<cnt2;i++)

int x,y;

x=minExtraPos(1);

y=minExtraPos(2);

a[x][y]=1;

a[y][x]=1;

extra[x]++;

extra[y]++;

laturi[++ind]=x;

laturi[++ind]=y;

for(int i=1;i<=n;i++)
viz[i]=0;

conex();

int mx=0;

for(int i=1;i<=n;i++)

if(extra[i]>mx)

mx=extra[i];

fout<<mx<<endl<<cnt2-1<<endl;

for(int i=1;i<=ind;i++)

fout<<laturi[i];

if(i%2==0)

fout<<endl;

else

fout<<" ";

return 0;

S-ar putea să vă placă și