program p31; const maxN = 1000; type PUzel = ^TUzel; TUzel = record hodnota: longint; levy, pravy: PUzel; delka, delkaVPodstromu: integer; end; TPoleVysek = array[1..maxN] of longint; var N: integer; { počet kopců } v: TPoleVysek; { výšky kopců } strom: PUzel; { pomocný strom} rostouciDo, rostouciZ: array[1..maxN] of integer; { délky nejdelších rostoucích posloupností končících (rostouciDo) a začínajících (rostouciZ) daným kopcem } maxDelka: integer; { délka nejdelší rostoucí posloupnosti } function max(a, b: integer): integer; begin if a > b then max := a else max := b; end; procedure nacti; { načte hodnoty ze vstupu } var i: integer; begin readln(N); for i := 1 to N do read(v[i]); end; function postavStrom(var pole: TPoleVysek; zacatek, konec: integer): PUzel; { vyrobí z hodnot pole[zacatek...konec] vyvážený binární vyhledávací strom, za předpokladu, že hodnoty v poli `pole' jsou setříděné. } var stred: integer; uzel: PUzel; begin if zacatek > konec then { interval zacatek-konec je prázdný, vrať prázdný strom } postavStrom := nil else begin stred := (zacatek + konec) div 2; { prostřední prvek } new(uzel); { vyrob uzel pro střed } uzel^.hodnota:= pole[stred]; uzel^.delka := 0; uzel^.delkaVPodstromu := 0; { rekurzivně postav oba synovské podstromy } uzel^.levy := postavStrom(pole, zacatek, stred - 1); uzel^.pravy := postavStrom(pole, stred + 1, konec); postavStrom := uzel; { vrať vytvořený uzel } end; end; procedure vymazStrom(uzel: PUzel); { nastaví všem uzlům nulové délky } begin if uzel <> nil then begin uzel^.delka := 0; uzel^.delkaVPodstromu := 0; vymazStrom(uzel^.levy); vymazStrom(uzel^.pravy); end; end; function nejdelsiNizsi(uzel: PUzel; hodnota: longint): integer; { najde maximum z délek uložených u uzlů s hodnotou menší nez zadaný parametr } var nejdelsi: integer; begin nejdelsi := 0; while uzel <> nil do begin { hledáme uzel s hodnotou `hodnota' } if uzel^.hodnota = hodnota then begin { podívej se do levého podstromu } if uzel^.levy <> nil then nejdelsi := max(nejdelsi, uzel^.levy^.delkaVPodstromu); uzel := nil; { konec cyklu } end else if uzel^.hodnota > hodnota then uzel := uzel^.levy { jen pokračujeme v hledání uzlu v levém podstromu } else begin {uzel^.hodnota < hodnota} { porovnej délky v levém podstromu i v uzlu samotném } nejdelsi := max(nejdelsi, uzel^.delka); if uzel^.levy <> nil then nejdelsi := max(nejdelsi, uzel^.levy^.delkaVPodstromu); { ... a pokračuj pravým podstromem } uzel := uzel^.pravy; end; end; nejdelsiNizsi := nejdelsi; end; function nejdelsiVyssi(uzel: PUzel; hodnota: longint): integer; { najde maximum z délek uložených u uzlů s hodnotou větší nez zadaný parametr } var nejdelsi: integer; begin nejdelsi := 0; while uzel <> nil do begin { hledáme uzel s hodnotou `hodnota' } if uzel^.hodnota = hodnota then begin { podívej se do pravého podstromu } if uzel^.pravy <> nil then nejdelsi := max(nejdelsi, uzel^.pravy^.delkaVPodstromu); uzel := nil; {konec cyklu} end else if uzel^.hodnota < hodnota then uzel := uzel^.pravy { jen pokračujeme v hledání uzlu v pravém podstromu } else begin {uzel^.hodnota > hodnota} { porovnej délky v pravém podstromu i v uzlu samotném } nejdelsi := max(nejdelsi, uzel^.delka); if uzel^.pravy <> nil then nejdelsi := max(nejdelsi, uzel^.pravy^.delkaVPodstromu); { ... a pokračuj levým podstromem } uzel := uzel^.levy; end; end; nejdelsiVyssi := nejdelsi; end; procedure nastavDelku(uzel: PUzel; hodnota: longint; delka: integer); { najde ve stromě uzel s hodnotou `hodnota' a nastaví mu zadanou délku } { také přepočítá příslušné hodnoty `delkaVPodstromu' } begin while uzel <> nil do begin { hledáme uzel s hodnotou `hodnota' } if uzel^.hodnota = hodnota then begin { našli jsme } uzel^.delka := delka; uzel^.delkaVPodstromu := max(uzel^.delkaVPodstromu, delka); uzel := nil; { konec cyklu } end else begin { uprav maximum v podstromu } uzel^.delkaVPodstromu := max(uzel^.delkaVPodstromu, delka); { ... a vyber následující uzel } if uzel^.hodnota < hodnota then uzel := uzel^.pravy { hodnota je v pravém podstromu } else uzel := uzel^.levy; { hodnota je v levém podstromu } end; end; end; procedure zaradDolu(var pole: TPoleVysek; index, maxIndex:integer); { používáno procedurou heapSort. `Pole' je reprezentace max-haldy v poli s délkou `maxIndex'; procedura zajistí, že prvek s indexem `index' "probublá" dolů až na správné místo -- tj. buď bude listem, nebo jeho synové budou mít nižší hodnoty. } var p: longint; j: integer; begin while index * 2 <= maxIndex do begin { do `j' dáme index syna s vyšší hodnotou } if index * 2 = maxIndex then j := index * 2 { jen jeden syn } else if pole[2*index] > pole[2*index + 1] then j := 2*index else j := 2*index + 1; { překontrolujeme podmínku } if pole[index] > pole[j] then index := maxIndex + 1 { podmínka splněna, ukončíme cyklus } else begin { podminka neplatí, vyměň hodnoty a pokračuj synem } p := pole[index]; pole[index]:= pole[j]; pole[j] :=p; index := j; end; end; end; procedure heapSort(var pole: TPoleVysek; delka: integer); var i, pul: integer; p: longint; begin { v poli si reprezentujeme haldu tak, že synové uzlu s indexem `i' mají index 2*i a 2*i+1. Toto je obvyklá reprezentace, pokud první prvek pole má index 1. } { postavíme maximovou haldu } pul := delka div 2; for i := pul downto 1 do zaradDolu(pole, i, delka); { od konce tvořime setříděnou posloupnost } for i := delka downto 2 do begin { odstraníme maximum } p := pole[1]; pole[1] := pole[i]; pole[i] := p; { obnovíme haldu } zaradDolu(pole, 1, i - 1); end; end; procedure pripravaStromu; { z hodnot v poli `v' vyrobí vyvážený strom } var i: integer; setridenePole: TPoleVysek; { pomocné pole } pocetUnikatnich: integer; { počet unikátních hodnot v pomocném poli } begin { nejprve hodnoty překopírujeme do pomocného pole } for i:= 1 to N do setridenePole[i] := v[i]; { pak je setřídíme } heapSort(setridenePole, N); { odstraníme duplicity v poli -- hodnoty v pomocném poli na indexech 1..pocetUnikatnich budou všechny hodnoty z původního pole, ale budou navzájem různé a setříděné. To snadno uděláme kontrolou sousedních prvků. } if N = 0 then pocetUnikatnich := 0 { žádné hodnoty, žádné nejsou unikátní } else begin pocetUnikatnich := 1; { první hodnota v poli je určitě unikátní } for i := 2 to N do if setridenePole[i] = setridenePole[pocetUnikatnich] then begin { hodnota je stejná jako předchozí, nic neděláme } end else begin { nová hodnota -- přidáme na konec } pocetUnikatnich := pocetUnikatnich + 1; setridenePole[pocetUnikatnich] := setridenePole[i]; end; end; { nakonec vyrobíme strom } strom := postavStrom(setridenePole, 1, pocetUnikatnich); end; procedure vypocetDelek; var i: integer; vyska: longint; delka: integer; begin maxDelka := 0; { nejprve počítáme délky posloupností končící daným kopcem } for i:= 1 to N do begin vyska := v[i]; delka := nejdelsiNizsi(strom, vyska) + 1; nastavDelku(strom, vyska, delka); rostouciDo[i] := delka; if delka > maxDelka then { zároveň zjistíme maximum } maxDelka := delka; end; { nyní spočítáme délky posloupností začínajících daným kopcem } vymazStrom(strom); for i:= N downto 1 do begin vyska := v[i]; delka := nejdelsiVyssi(strom, vyska) + 1; nastavDelku(strom, vyska, delka); rostouciZ[i] := delka; end; end; procedure vypis; var i: integer; pocty: array[1..maxN] of integer; { pro každé `i' obsahuje počet vrcholů, které mohou být na i-tém místě v nejdelší rostoucí podposloupnosti } begin { vynulujeme pole } for i := 1 to N do pocty[i] := 0; { spočteme hodnoty pole pocty } for i := 1 to N do if rostouciDo[i] + rostouciZ[i] - 1 = maxDelka then { leží v nějaké nejdelší posloupnosti } pocty[rostouciDo[i]] := pocty[rostouciDo[i]] + 1; for i := 1 to N do begin if i > 1 then write(' '); if rostouciDo[i] + rostouciZ[i] - 1 = maxDelka then begin if pocty[rostouciDo[i]] = 1 then write('musím') else write('mohu') end else write('nemohu'); end; writeln; end; begin nacti; pripravaStromu; vypocetDelek; vypis; end.