#include <iostream> #include <set> #include <string> #include <vector> using namespace std; struct Punkt { int x; int y; }; struct Sciezka { vector< Punkt > dane; int ostatnie_wyburzenie = 0; bool czy_zawiera_punkt( Punkt const& p ) const { int odl = p.x + p.y; return (dane[odl].x == p.x) && (dane[odl].y == p.y); } bool czy_za_wyburzeniem( Punkt const& p ) const { int odl = p.x + p.y; return ostatnie_wyburzenie < odl; } void oznacz_wyburzenie( Punkt const& p ) { int odl = p.x + p.y; ostatnie_wyburzenie = odl; } }; struct Plansza { set< long long > dane; char pole(int x, int y) const { long long idx = indeks_pola( x, y ); return (dane.find( idx ) == dane.end()) ? '.' : '#'; } int const szer; int const wys; Plansza(int szer, int wys) : szer(szer), wys(wys) {}; int indeks_pola(int x, int y) const { return 1LL * x + ( 1LL * y * szer ); }; int dlugosc_szlaku() const { return szer + wys - 1; } bool w_dol_pusty( Punkt& p ) const { if( p.y == this->wys - 1 ) { return false; }; char ozn = this->pole( p.x, p.y + 1); if( ozn == '.' ) { p.y += 1; return true; } return false; } bool w_prawo_pusty( Punkt& p ) const { if( p.x == this->szer - 1 ) { return false; }; char ozn = this->pole( p.x + 1, p.y ); if( ozn == '.' ) { p.x += 1; return true; } return false; } void buduj(Punkt p) { long long idx = indeks_pola( p.x, p.y ); dane.insert( idx ); } void zburz(Punkt p) { long long idx = indeks_pola( p.x, p.y ); dane.erase( idx ); } }; struct Atak { Punkt punkt; int modyfikator; }; istream& operator>>( istream& is, Atak& a ) { // Najpierw podawany jest r (row/wiersz) a wiec y is >> a.punkt.y >> a.punkt.x >> a.modyfikator; return is; } struct Tresc { int wys_n; int szer_m; vector< Atak > ataki; }; Tresc czytaj_tresc( istream& istr ) { Tresc t; istr >> t.wys_n; istr >> t.szer_m; int l_atakow; istr >> l_atakow; t.ataki.resize( l_atakow ); for ( int i = 0; i < l_atakow; ++i ) { istr >> t.ataki[i]; } return t; }; Sciezka znajdz_pierwsza_sciezke( Plansza const& pla ) { Sciezka s; s.dane.reserve( pla.szer + pla.wys ); for( int y = 0; y < pla.wys; ++y ) { s.dane.push_back( { 0, y } ); }; for( int x = 1; x < pla.szer; ++x ) { s.dane.push_back( { x, pla.wys - 1 } ); } return s; } char kierunek_powrotu( Punkt const& pocz, Punkt const& kon ) { if( pocz.x == kon.x ) { return '^'; } if( pocz.y == kon.y ) { return '<'; } //assert(false); return '<'; // uspokoj kompilator } bool hack( vector< int > const& v, Punkt const& p ) { if( p.x < v[p.y] ) { return true; } return false; }; Sciezka znajdz_sciezke( Plansza& pla, Sciezka const& sciezka, Punkt pocz ) { Sciezka ns; int odl = pocz.x + pocz.y; // musze zrobic kopie, bo jesli nie znajde nowej sciezki // to stara sciezka musi zostac (atak bajtogromu!) copy( sciezka.dane.begin(), sciezka.dane.begin() + odl, back_inserter( ns.dane )); Punkt ostatni = sciezka.dane[odl]; char ostatni_kier = kierunek_powrotu( ns.dane.back(), ostatni ); if( sciezka.czy_za_wyburzeniem( pocz ) ) { // jesli juz tu szukalem, to nie szukam ponownie ns.ostatnie_wyburzenie = sciezka.ostatnie_wyburzenie; }; vector< int > zbadane( pla.wys ); while( (ns.dane.size() > ns.ostatnie_wyburzenie) && (ns.dane.size() < pla.dlugosc_szlaku()) ) { bool wycofaj = false; if( ostatni_kier == '<' ) { /* Jesli sie cofam od tej strony to w kolejnym kroku rowniez bede musial sie cofnac. To kolejne cofniecie moze byc w lewo '<' albo do gory '^'. Jesli bedzie do gory to bede mial szanse sprobowac kroku w prawo '>'. */ wycofaj = true; } else if( ostatni_kier == '^' ) { // sprobuj w prawo Punkt nastepny = ns.dane.back(); bool zbadany_z_prawej = hack( zbadane, nastepny ); if( (! zbadany_z_prawej) && pla.w_prawo_pusty(nastepny )) { ns.dane.push_back( nastepny ); ostatni_kier = '>'; } else { wycofaj = true; } } else if( (ostatni_kier == '>') || (ostatni_kier == 'v') ) { Punkt nastepny = ns.dane.back(); bool zbadany_z_prawej = hack( zbadane, nastepny ); if( pla.w_dol_pusty( nastepny ) ) { ns.dane.push_back( nastepny ); ostatni_kier = 'v'; } else if( (!zbadany_z_prawej) && pla.w_prawo_pusty( nastepny ) ) { ns.dane.push_back( nastepny ); ostatni_kier = '>'; } else { wycofaj = true; } } if( wycofaj ) { ostatni = ns.dane.back(); ns.dane.pop_back(); zbadane[ostatni.y] = min( zbadane[ostatni.y], ostatni.x ); if( ns.dane.size() > 0 ) { ostatni_kier = kierunek_powrotu( ns.dane.back(), ostatni ); } } else { // zatem zrobilem krok if( ns.dane.size() > odl ) { // zaszedlem za potencjalne wyburzenie/ostatni atak if( sciezka.czy_zawiera_punkt( ns.dane.back() ) ) { // i trafilem na stary trop... // zatem kopiuje ze starej sciezki.. int brakuje = sciezka.dane.size() - ns.dane.size(); copy( sciezka.dane.end() - brakuje, sciezka.dane.end(), back_inserter( ns.dane )); } } } } return ns; } vector< bool > rozwiaz_zadanie( Tresc const& t ) { Plansza pla( t.szer_m, t.wys_n); Sciezka sci = znajdz_pierwsza_sciezke( pla ); vector< bool > odp; int modyfikator = 0; for( auto&& ata : t.ataki ) { Punkt const pkt { (ata.punkt.x ^ modyfikator) % pla.szer, (ata.punkt.y ^ modyfikator) % pla.wys, }; pla.buduj( pkt ); if( sci.czy_zawiera_punkt( pkt ) ) { pla.buduj( pkt ); Sciezka ns = znajdz_sciezke( pla, sci, pkt ); if( ns.dane.size() ) { sci = ns; pla.buduj( pkt ); odp.push_back( false ); } else { pla.zburz( pkt ); odp.push_back( true ); } } else { pla.buduj( pkt ); odp.push_back( false ); } if( odp.back() ) { pla.zburz(pkt); modyfikator = modyfikator ^ ata.modyfikator; } } return odp; } int main() { Tresc t = czytaj_tresc( cin ); vector< bool > odp = rozwiaz_zadanie( t ); for( auto o: odp ) { cout << (o? "TAK\n" : "NIE\n"); } };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | #include <iostream> #include <set> #include <string> #include <vector> using namespace std; struct Punkt { int x; int y; }; struct Sciezka { vector< Punkt > dane; int ostatnie_wyburzenie = 0; bool czy_zawiera_punkt( Punkt const& p ) const { int odl = p.x + p.y; return (dane[odl].x == p.x) && (dane[odl].y == p.y); } bool czy_za_wyburzeniem( Punkt const& p ) const { int odl = p.x + p.y; return ostatnie_wyburzenie < odl; } void oznacz_wyburzenie( Punkt const& p ) { int odl = p.x + p.y; ostatnie_wyburzenie = odl; } }; struct Plansza { set< long long > dane; char pole(int x, int y) const { long long idx = indeks_pola( x, y ); return (dane.find( idx ) == dane.end()) ? '.' : '#'; } int const szer; int const wys; Plansza(int szer, int wys) : szer(szer), wys(wys) {}; int indeks_pola(int x, int y) const { return 1LL * x + ( 1LL * y * szer ); }; int dlugosc_szlaku() const { return szer + wys - 1; } bool w_dol_pusty( Punkt& p ) const { if( p.y == this->wys - 1 ) { return false; }; char ozn = this->pole( p.x, p.y + 1); if( ozn == '.' ) { p.y += 1; return true; } return false; } bool w_prawo_pusty( Punkt& p ) const { if( p.x == this->szer - 1 ) { return false; }; char ozn = this->pole( p.x + 1, p.y ); if( ozn == '.' ) { p.x += 1; return true; } return false; } void buduj(Punkt p) { long long idx = indeks_pola( p.x, p.y ); dane.insert( idx ); } void zburz(Punkt p) { long long idx = indeks_pola( p.x, p.y ); dane.erase( idx ); } }; struct Atak { Punkt punkt; int modyfikator; }; istream& operator>>( istream& is, Atak& a ) { // Najpierw podawany jest r (row/wiersz) a wiec y is >> a.punkt.y >> a.punkt.x >> a.modyfikator; return is; } struct Tresc { int wys_n; int szer_m; vector< Atak > ataki; }; Tresc czytaj_tresc( istream& istr ) { Tresc t; istr >> t.wys_n; istr >> t.szer_m; int l_atakow; istr >> l_atakow; t.ataki.resize( l_atakow ); for ( int i = 0; i < l_atakow; ++i ) { istr >> t.ataki[i]; } return t; }; Sciezka znajdz_pierwsza_sciezke( Plansza const& pla ) { Sciezka s; s.dane.reserve( pla.szer + pla.wys ); for( int y = 0; y < pla.wys; ++y ) { s.dane.push_back( { 0, y } ); }; for( int x = 1; x < pla.szer; ++x ) { s.dane.push_back( { x, pla.wys - 1 } ); } return s; } char kierunek_powrotu( Punkt const& pocz, Punkt const& kon ) { if( pocz.x == kon.x ) { return '^'; } if( pocz.y == kon.y ) { return '<'; } //assert(false); return '<'; // uspokoj kompilator } bool hack( vector< int > const& v, Punkt const& p ) { if( p.x < v[p.y] ) { return true; } return false; }; Sciezka znajdz_sciezke( Plansza& pla, Sciezka const& sciezka, Punkt pocz ) { Sciezka ns; int odl = pocz.x + pocz.y; // musze zrobic kopie, bo jesli nie znajde nowej sciezki // to stara sciezka musi zostac (atak bajtogromu!) copy( sciezka.dane.begin(), sciezka.dane.begin() + odl, back_inserter( ns.dane )); Punkt ostatni = sciezka.dane[odl]; char ostatni_kier = kierunek_powrotu( ns.dane.back(), ostatni ); if( sciezka.czy_za_wyburzeniem( pocz ) ) { // jesli juz tu szukalem, to nie szukam ponownie ns.ostatnie_wyburzenie = sciezka.ostatnie_wyburzenie; }; vector< int > zbadane( pla.wys ); while( (ns.dane.size() > ns.ostatnie_wyburzenie) && (ns.dane.size() < pla.dlugosc_szlaku()) ) { bool wycofaj = false; if( ostatni_kier == '<' ) { /* Jesli sie cofam od tej strony to w kolejnym kroku rowniez bede musial sie cofnac. To kolejne cofniecie moze byc w lewo '<' albo do gory '^'. Jesli bedzie do gory to bede mial szanse sprobowac kroku w prawo '>'. */ wycofaj = true; } else if( ostatni_kier == '^' ) { // sprobuj w prawo Punkt nastepny = ns.dane.back(); bool zbadany_z_prawej = hack( zbadane, nastepny ); if( (! zbadany_z_prawej) && pla.w_prawo_pusty(nastepny )) { ns.dane.push_back( nastepny ); ostatni_kier = '>'; } else { wycofaj = true; } } else if( (ostatni_kier == '>') || (ostatni_kier == 'v') ) { Punkt nastepny = ns.dane.back(); bool zbadany_z_prawej = hack( zbadane, nastepny ); if( pla.w_dol_pusty( nastepny ) ) { ns.dane.push_back( nastepny ); ostatni_kier = 'v'; } else if( (!zbadany_z_prawej) && pla.w_prawo_pusty( nastepny ) ) { ns.dane.push_back( nastepny ); ostatni_kier = '>'; } else { wycofaj = true; } } if( wycofaj ) { ostatni = ns.dane.back(); ns.dane.pop_back(); zbadane[ostatni.y] = min( zbadane[ostatni.y], ostatni.x ); if( ns.dane.size() > 0 ) { ostatni_kier = kierunek_powrotu( ns.dane.back(), ostatni ); } } else { // zatem zrobilem krok if( ns.dane.size() > odl ) { // zaszedlem za potencjalne wyburzenie/ostatni atak if( sciezka.czy_zawiera_punkt( ns.dane.back() ) ) { // i trafilem na stary trop... // zatem kopiuje ze starej sciezki.. int brakuje = sciezka.dane.size() - ns.dane.size(); copy( sciezka.dane.end() - brakuje, sciezka.dane.end(), back_inserter( ns.dane )); } } } } return ns; } vector< bool > rozwiaz_zadanie( Tresc const& t ) { Plansza pla( t.szer_m, t.wys_n); Sciezka sci = znajdz_pierwsza_sciezke( pla ); vector< bool > odp; int modyfikator = 0; for( auto&& ata : t.ataki ) { Punkt const pkt { (ata.punkt.x ^ modyfikator) % pla.szer, (ata.punkt.y ^ modyfikator) % pla.wys, }; pla.buduj( pkt ); if( sci.czy_zawiera_punkt( pkt ) ) { pla.buduj( pkt ); Sciezka ns = znajdz_sciezke( pla, sci, pkt ); if( ns.dane.size() ) { sci = ns; pla.buduj( pkt ); odp.push_back( false ); } else { pla.zburz( pkt ); odp.push_back( true ); } } else { pla.buduj( pkt ); odp.push_back( false ); } if( odp.back() ) { pla.zburz(pkt); modyfikator = modyfikator ^ ata.modyfikator; } } return odp; } int main() { Tresc t = czytaj_tresc( cin ); vector< bool > odp = rozwiaz_zadanie( t ); for( auto o: odp ) { cout << (o? "TAK\n" : "NIE\n"); } }; |