/*************************************************************************
* *
* XX Olimpiada Informatyczna *
* *
* Zadanie: Multidrink *
* Autor: Przemyslaw Horban *
* Zlozonosc czasowa: O(n) *
* Opis: Rozwiazanie wzorcowe, *
* programowanie dynamiczne *
* *
*************************************************************************/
#include<iostream>
#include<set>
#include<iomanip>
#include<sstream>
#include<fstream>
#include<stack>
#include<cstdio>
#include<cmath>
#include<cassert>
#include<queue>
#include<vector>
#include<list>
#include<algorithm>
#include<map>
#include<cstring>
#include<cctype>
#define DEBUG 0
#define deb(x) if(DEBUG) { cout << #x << " = " << (x) << endl; }
using namespace std;
#define fe(e,C) for(__typeof((C).begin()) e = (C).begin(); e != (C).end(); e++)
#define foreach(VAR_DEF, C) \
fe(__varname, C) \
for(bool run_once = true; run_once; ) \
for(VAR_DEF = *(__varname); run_once; run_once = false)
#define fi(i,n) for(int (i) = 0, __end = (n); (i) < __end; i++)
#define iterate_until(i,s,n) for(int (i) = (s), __end = (n); (i) < __end; i++)
#define iterate_to(i,a,b) ft(i,a,b)
#define ft(i,a,b) for(int (i) = (a), __end = (b); (i) <= __end; (i)++)
#define ALL(V) (V).begin(), (V).end()
#define SET(T, c) memset(T, c, sizeof(T))
#define UNIQUE(V) (V).erase(unique(ALL(V)), (V).end())
class Drzewo {
public:
vector<vector<int> > E;
Drzewo(FILE *input, int zmniejszenieIndeksow) {
int n;
if(1 != scanf("%d", &n))
exit(-1);
E.clear();
E.resize(n);
iterate_until(i, 0, n-1) {
int u, v;
if(2 != scanf("%d%d", &u, &v))
exit(-1);
u -= zmniejszenieIndeksow;
v -= zmniejszenieIndeksow;
E[u].push_back(v);
E[v].push_back(u);
}
}
vector<int> sciezka(int u, int v) const {
assert(0 <= u && u < rozmiar());
assert(0 <= v && v < rozmiar());
assert(v != u);
vector<int> sciezka;
sciezka.push_back(u);
if(przedluzDo(&sciezka, v))
return sciezka;
else
return vector<int>();
}
int rozmiar() const {
return E.size();
}
private:
bool przedluzDo(vector<int> *sciezkaPtr, int cel) const {
vector<int> &sciezka = *sciezkaPtr;
assert(!sciezka.empty());
int koniec = sciezka.back();
int przedOstatni = (sciezka.size() > 1 ? *(sciezka.end() - 2) : -1);
if(koniec == cel)
return true;
foreach(int nast, E[koniec]) {
if(nast != przedOstatni) {
sciezka.push_back(nast);
if(przedluzDo(sciezkaPtr, cel))
return true;
sciezka.pop_back();
}
}
return false;
}
};
class Rozpiecie {
public:
vector<vector<int> > synowie;
vector<int> korzenie;
Rozpiecie(const Drzewo &D, int u, int v) {
synowie.clear();
synowie.resize(D.rozmiar());
korzenie = D.sciezka(u, v);
iterate_until(i, 0, korzenie.size()) {
int korzenNaLewo = -1,
korzen = korzenie[i],
korzenNaPrawo = -1;
if(i > 0)
korzenNaLewo = korzenie[i - 1];
if(i + 1 < (int)korzenie.size())
korzenNaPrawo = korzenie[i + 1];
dodajSynow(korzen, D, korzenNaLewo, korzenNaPrawo);
}
}
size_t dlugosc() const {
return korzenie.size();
}
size_t liczbaWezlow() const {
return synowie.size();
}
private:
void dodajSynow(int korzen, const Drzewo &D,
int unikaj1, int unikaj2) {
foreach(int nast, D.E[korzen]) {
if(nast != unikaj1 && nast != unikaj2) {
synowie[korzen].push_back(nast);
dodajSynow(nast, D, korzen, -1);
}
}
}
};
// Zaczynamy ze zbiorem sciezek jednoelementowych.
// sklej(u, v), laczy konce dwoch sciezek, tworzac jedna
// przejdz(u), zaczyna w u, ktory musi byc poczatkiem sciezki
// i zwraca wektor zawierajacy jej elementy.
class SciezkiDwukierunkowe {
vector<char> liczbaSklejen;
vector<int> nastepnikoPoprzednik;
public:
SciezkiDwukierunkowe(int rozmiar)
: liczbaSklejen(rozmiar, 0), nastepnikoPoprzednik(rozmiar, 0) {}
void sklej(int u, int v) {
assert(liczbaSklejen[u] < 2);
assert(liczbaSklejen[v] < 2);
liczbaSklejen[u] += 1;
liczbaSklejen[v] += 1;
nastepnikoPoprzednik[u] ^= v;
nastepnikoPoprzednik[v] ^= u;
}
vector<int> przejdz(int u) const {
assert(0 <= u && u < (int)liczbaSklejen.size());
assert(liczbaSklejen[u] < 2);
vector<int> sciezka;
sciezka.push_back(u);
if(liczbaSklejen[u] == 0)
return sciezka;
// liczbaSklejen[u] == 1
sciezka.push_back(nastepnikoPoprzednik[u]);
while(liczbaSklejen[sciezka.back()] == 2) {
int przedOstatni = *(sciezka.end() - 2);
int ostati = sciezka.back();
int nastepny = nastepnikoPoprzednik[ostati] ^ przedOstatni;
sciezka.push_back(nastepny);
}
return sciezka;
}
};
enum TypSciezkiPoDrzewie {
Z1Do1 = 0, // pojedynczy wezel
Z1Do2 = 1, // z korzenia do potomka
Z2Do2 = 2, // miedzy 2 potomkami korzenia
Z0Do0 = 3 // nie istnieje zadne powyzsze
};
const int LICZBA_TYPOW_SCIEZEK = 4;
class SciezkiRozpiecia {
public:
SciezkiRozpiecia(const Rozpiecie &R): sciezki(R.liczbaWezlow()) {
foreach(int korzen, R.korzenie)
sciezkiWDrzewach.push_back(wyznaczSciezke(R, korzen));
}
TypSciezkiPoDrzewie sciezkaPo(int nrDrzewa, vector<int> *sciezkaWsk) const {
assert(0 <= nrDrzewa && nrDrzewa < (int)sciezkiWDrzewach.size());
sciezkaWsk->clear();
int poczatek = sciezkiWDrzewach[nrDrzewa].poczatek;
if(poczatek >= 0) {
*sciezkaWsk = sciezki.przejdz(poczatek);
assert(sciezkaWsk->back() == sciezkiWDrzewach[nrDrzewa].koniec);
}
else {
assert(sciezkiWDrzewach[nrDrzewa].typ == Z0Do0);
}
return sciezkiWDrzewach[nrDrzewa].typ;
}
private:
struct Sciezka {
Sciezka(TypSciezkiPoDrzewie typ, int pocz, int kon)
: typ(typ), poczatek(pocz), koniec(kon) {}
TypSciezkiPoDrzewie typ;
int poczatek, koniec;
};
// Zawiera przebiegi sciezek o poczatkach i koncach
// w sciezkiWDrzewach.
SciezkiDwukierunkowe sciezki;
vector<Sciezka> sciezkiWDrzewach;
Sciezka wyznaczSciezke(const Rozpiecie &R, int korzen) {
vector<Sciezka> sciezkiTypu[LICZBA_TYPOW_SCIEZEK];
foreach(int syn, R.synowie[korzen]) {
const Sciezka &s = wyznaczSciezke(R, syn);
sciezkiTypu[s.typ].push_back(s);
}
// W pierwszych trzech przypadkach, nie da sie
// skonstuowac sciezki w poddrzewie
if(!sciezkiTypu[Z0Do0].empty())
return Sciezka(Z0Do0, -1, -1);
if(!sciezkiTypu[Z2Do2].empty())
return Sciezka(Z0Do0, -1, -1);
if(sciezkiTypu[Z1Do2].size() > 2)
return Sciezka(Z0Do0, -1, -1);
if(sciezkiTypu[Z1Do2].size() == 2) {
// - zaczynamy 1 w dol
// - z 2 w dol do korzenia
// - z korzenia 2 w dol
// - konczymy 1 w dol
// - przeskakujemy jedynki
Sciezka &z1Do2a = sciezkiTypu[Z1Do2][0];
Sciezka &z1Do2b = sciezkiTypu[Z1Do2][1];
Sciezka s(Z2Do2, -1, -1);
s.poczatek = z1Do2a.poczatek;
sciezki.sklej(z1Do2a.koniec, korzen);
sciezki.sklej(korzen, z1Do2b.koniec);
int ostatniKoniec = z1Do2b.poczatek;
foreach(Sciezka &z1Do1, sciezkiTypu[Z1Do1]) {
sciezki.sklej(ostatniKoniec, z1Do1.poczatek);
ostatniKoniec = z1Do1.koniec;
}
s.koniec = ostatniKoniec;
return s;
}
if(sciezkiTypu[Z1Do2].size() +
sciezkiTypu[Z1Do1].size() > 0) {
// - zaczynamy w korzeniu
// - idziemy do poddrzewa 1-2, konczac w jego korzeniu
// - przechodzimy jedynki
Sciezka s(Z1Do2, -1, -1);
s.poczatek = korzen;
int ostatniKoniec = korzen;
if(!sciezkiTypu[Z1Do2].empty()) {
sciezki.sklej(korzen, sciezkiTypu[Z1Do2][0].koniec);
ostatniKoniec = sciezkiTypu[Z1Do2][0].poczatek;
}
foreach(Sciezka &z1Do1, sciezkiTypu[Z1Do1]) {
sciezki.sklej(ostatniKoniec, z1Do1.poczatek);
ostatniKoniec = z1Do1.koniec;
}
s.koniec = ostatniKoniec;
return s;
}
return Sciezka(Z1Do1, korzen, korzen);
}
};
vector<int> znajdzSciezkePoRozpieciu(const Rozpiecie &R) {
vector<int> sciezka;
bool koniecWKorzeniu = false;
SciezkiRozpiecia sciezkiDrzew(R);
iterate_until(nrDrzewa, 0, R.dlugosc()) {
vector<int> fragment;
TypSciezkiPoDrzewie typ = sciezkiDrzew.sciezkaPo(nrDrzewa, &fragment);
deb(typ);
if(typ == Z0Do0)
return vector<int>();
if(typ == Z1Do1) {
sciezka.insert(sciezka.end(), ALL(fragment));
koniecWKorzeniu = true;
}
if(!koniecWKorzeniu) {
if(typ == Z2Do2)
return vector<int>();
if(typ == Z1Do2) {
sciezka.insert(sciezka.end(), ALL(fragment));
koniecWKorzeniu = false;
}
}
else {
if(typ == Z2Do2) {
sciezka.insert(sciezka.end(), ALL(fragment));
koniecWKorzeniu = false;
}
if(typ == Z1Do2) {
sciezka.insert(sciezka.end(), fragment.rbegin(), fragment.rend());
koniecWKorzeniu = true;
}
}
}
if(koniecWKorzeniu)
return sciezka;
else
return vector<int>();
}
void wypisz(const vector<int> &wartosci, int przesuniecie) {
foreach(int w, wartosci)
printf("%d\n", w + przesuniecie);
}
int main() {
Drzewo D(stdin, 1);
int pierwszyBar = 0,
ostatniBar = D.rozmiar() - 1;
Rozpiecie R(D, pierwszyBar, ostatniBar);
vector<int> sciezka = znajdzSciezkePoRozpieciu(R);
if(!sciezka.empty())
wypisz(sciezka, +1);
else
puts("BRAK");
return 0;
}
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
2 |
Correct |
0 ms |
2036 KB |
Output is correct |
3 |
Correct |
0 ms |
2036 KB |
Output is correct |
4 |
Correct |
0 ms |
2036 KB |
Output is correct |
5 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
2 |
Correct |
0 ms |
2036 KB |
Output is correct |
3 |
Correct |
0 ms |
2036 KB |
Output is correct |
4 |
Correct |
0 ms |
2036 KB |
Output is correct |
5 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
2 |
Correct |
0 ms |
2036 KB |
Output is correct |
3 |
Correct |
0 ms |
2036 KB |
Output is correct |
4 |
Correct |
0 ms |
2036 KB |
Output is correct |
5 |
Correct |
0 ms |
2036 KB |
Output is correct |
6 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
2 |
Correct |
0 ms |
2036 KB |
Output is correct |
3 |
Correct |
0 ms |
2036 KB |
Output is correct |
4 |
Correct |
0 ms |
2036 KB |
Output is correct |
5 |
Correct |
0 ms |
2036 KB |
Output is correct |
6 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
2 |
Correct |
0 ms |
2036 KB |
Output is correct |
3 |
Correct |
0 ms |
2036 KB |
Output is correct |
4 |
Correct |
0 ms |
2036 KB |
Output is correct |
5 |
Correct |
0 ms |
2036 KB |
Output is correct |
6 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
2 |
Correct |
0 ms |
2036 KB |
Output is correct |
3 |
Correct |
0 ms |
2036 KB |
Output is correct |
4 |
Correct |
0 ms |
2036 KB |
Output is correct |
5 |
Correct |
0 ms |
2036 KB |
Output is correct |
6 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
0 ms |
2168 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
3 ms |
2656 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
3 ms |
2508 KB |
Output is correct |
2 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
179 ms |
13888 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
113 ms |
14428 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
113 ms |
12140 KB |
Output is correct |
2 |
Correct |
0 ms |
2036 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
639 ms |
58856 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
629 ms |
60672 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
899 ms |
57984 KB |
Output is correct |
2 |
Correct |
766 ms |
61380 KB |
Output is correct |
# |
결과 |
실행 시간 |
메모리 |
Grader output |
1 |
Correct |
916 ms |
114452 KB |
Output is correct |
2 |
Correct |
713 ms |
117348 KB |
Output is correct |