const Modulus = 469762049; {7*2^26+1, je to prvočíslo} Koren26 = 33; {2^26-tý kořen jedničky na okruhu modulo 469762049} Ln10_2 = ln(10)/ln(2); Ln2 = ln(2); Epsilon = 1E-7; {Korekce na přesnost floatových operací} type PVelkeCislo = ^TVelkeCislo; TVelkeCislo = object private Cifry:array of integer; public constructor Nove(Delka:integer); destructor Zrus; procedure Pricti(Co:PVelkeCislo); procedure Vynasob(Cim:PVelkeCislo); procedure NastavHodnotu(Kolik:integer); procedure Vypis; end; var n:integer; Exponent2:integer; KorenJednicky,InverzeKorene,InverzePoctuCifer:int64; {Předpočítané konstanty pro FFT} VelkySef:integer; {Nadřízený všech uvažovaných zaměstnanců} Podrizeni:array of integer; {První podřizený daného pracovníka, -1 pokud neexistuje} Spolupracovnici:array of integer; {Další podřízený stejného šéfa ... -1 pokud byl tenhle poslední} DelkaCisel:integer; {Počet cifer čísel} PocetSkupin:PVelkeCislo; {Kolika způsoby lze zaměstnance rozdělit} PocetPodstromu:PVelkecislo; Jednicka:PVelkeCislo; {1 ... jen zapsaná jako dlouhé číslo, bude se vyskytovat několikrát} PomocnaCisla:array of PVelkeCislo; constructor TVelkeCislo.Nove(Delka:integer); begin SetLength(Cifry,Delka); {Jen připraví místo na cifry} end; destructor TVelkeCislo.Zrus; {Prázdný, freepascal se o dynamické pole v objektu stará sám, ale je třeba zavolat destruktor} begin end; procedure TVelkeCislo.Pricti(Co:PVelkeCislo); {Předpokládá stejnou délku čísel a že výsledek se vejde do této délky čísla} var i,Soucet,Prenos:integer; begin Prenos:=0; for i:=0 to Length(Cifry)-1 do begin Soucet:=Cifry[i]+Co^.Cifry[i]+Prenos; Cifry[i]:=Soucet mod 10; Prenos:=Soucet div 10; end; {Po doběhnutí z předpokladů má být Prenos=0} end; function Umocni(Co,NaKolik,Modulus:int64):int64; {Spočte Co^NaKolik mod Modulus} var Vysledek:int64; {Předpokládá, že Co se vejde do longintu, int64 kvůli velikosti druhé mocniny} {V čase O(log(Nakolik)), pomocí rekurzivního mocnění na druhou} {Šlo by předpočítat, používáme jen pro inicializaci} begin if NaKolik = 0 then Umocni:=1 else begin Vysledek:=Umocni((Co*Co) mod Modulus,NaKolik div 2,Modulus); if Nakolik mod 2 =1 then Vysledek:=Vysledek*Co mod Modulus; Umocni:=Vysledek; end; end; procedure FFT_Obecne(Original:array of int64;var Vysledek:array of int64;Zaklad:int64); {Cooleyův-Tukeyův algoritmus pro výpočet Fourierovy transformace s daným základem, čas O(N log(N))} {FT je počítána na okruhu modulo 469762049} {Vstup i výsledky se sice vejdou do longintu, int64 je ale nutný aby nedošlo k přetečení při násobení.} {Dynamické pole, resp. prostor, který alokují, uklízí freepascal na konci procedury sám} var i:integer; Liche,Sude:array of int64; SudeFFT,LicheFFT:array of int64; PulkaDelky:longint; NovyZaklad:int64; Alfa_J:int64; begin if Length(Original)=1 then Vysledek[0]:=Original[0] {FT z jednoho prvku je triviální} else begin PulkaDelky:=Length(Original) div 2; SetLength(Sude,PulkaDelky); {Rozdělíme vstup na sudé a liché prvky} SetLength(Liche,PulkaDelky); for i:=0 to Length(Sude)-1 do begin Sude[i]:=Original[2*i]; Liche[i]:=Original[2*i+1]; end; SetLength(SudeFFT,PulkaDelky); SetLength(LicheFFT,PulkaDelky); NovyZaklad:=(Zaklad*Zaklad) mod Modulus; FFT_Obecne(Sude,SudeFFT,NovyZaklad); {Na těchto půlkách spočteme FT s odpovídajícím základem} FFT_Obecne(Liche,LicheFFT,NovyZaklad); {A pak dopočteme FT původního vzorku} {F[i] = Sude[i] + Liche[i]*Zaklad^i} {F[i+N/2] = Sude[i] + Liche[i]*Zaklad^(i+N/2)} Alfa_J:=1; {Zde bude Zaklad^i} for i:=0 to PulkaDelky-1 do begin Vysledek[i]:=(LicheFFT[i]*Alfa_J) mod Modulus; Vysledek[i+PulkaDelky]:=(LicheFFT[i]*Alfa_J) mod Modulus; Alfa_J:=(Alfa_J*Zaklad) mod Modulus; end; {V Alfa_J je nyní Zaklad^(N/2)} for i:=0 to PulkaDelky-1 do begin Vysledek[i]:=(Vysledek[i]+SudeFFT[i]) mod Modulus; Vysledek[i+PulkaDelky]:=(Vysledek[i+PulkaDelky]*Alfa_J+SudeFFT[i]) mod Modulus; end; end; end; procedure FFT(Original:PVelkeCislo;var Vysledek:array of int64); {FT z cifer daného čísla pomocí předchozí procedury} var PomocnePole:array of int64; i:integer; begin SetLength(PomocnePole,Length(Original^.Cifry)); for i:=0 to Length(Original^.Cifry)-1 do PomocnePole[i]:=Original^.Cifry[i]; FFT_Obecne(PomocnePole,Vysledek,KorenJednicky); {FT} end; procedure IFFT(Original:array of int64;Vysledek:PVelkeCislo); {Inverzní FT z daných frekvenčních koeficientů, od příme FT se liší Inverzním základem a normalizací} var PomocnePole:array of int64; i:integer; begin SetLength(PomocnePole,Length(Vysledek^.Cifry)); FFT_Obecne(Original,PomocnePole,InverzeKorene); {Inverzní FT} for i:=0 to Length(Vysledek^.Cifry)-1 do {Normalizace} Vysledek^.Cifry[i]:=(PomocnePole[i]*InverzePoctuCifer) mod Modulus; end; procedure TVelkeCislo.Vynasob(Cim:PVelkeCislo); {Schönhageův-Strassenův algoritmus pro násobení dlouhých čísel, čas O(N log N log log N)} {Vychází z ideje, že vynásobení frekvenčních obrazů po složkách, odpovídá to konvoluci v přímém obrazu} var F1,F2:array of int64; i,Prenos,Soucet:integer; begin SetLength(F1,Length(Cifry)); SetLength(F2,Length(Cifry)); FFT(@Self,F1); {FT jednotlivých komponent} FFT(Cim,F2); for i:=0 to Length(F1)-1 do F1[i]:=(F1[i]*F2[i]) mod Modulus; {Spočteme součin po složkách} IFFT(F1,@Self); {Inverzní FT dostaneme "konvoluci" původních čísel} Prenos:=0; for i:=0 to Length(Cifry) do begin {A teď jen opravíme koeficienty aby opět byly jen cifry} Soucet:=Cifry[i]+Prenos; Prenos:=Soucet div 10; Cifry[i]:=Soucet mod 10; end; end; procedure TVelkeCislo.Vypis; {Vypíše číslo} var i:integer; begin for i:=Length(Cifry)-1 downto 0 do if Cifry[i]<>0 then break; while i>=0 do begin write(Cifry[i]); dec(i); end; end; procedure TVelkeCislo.NastavHodnotu(Kolik:integer); var i:integer; begin if Kolik<0 then Kolik:=0; for i:=0 to Length(Cifry)-1 do begin {Nastavení příslušných cifer} Cifry[i]:=Kolik mod 10; Kolik:=Kolik div 10; end; end; procedure Nacti; var Sef:integer; i:integer; begin readln(n); SetLength(Podrizeni,n+1); for i:=1 to n do Podrizeni[i]:=-1; SetLength(Spolupracovnici,n+1); for i:=1 to n do begin readln(Sef); {Postupně přidáváme zaměstanance do seznamu podřízených} if Sef=-1 then begin Spolupracovnici[i]:=-1; VelkySef:=i; end else begin Spolupracovnici[i]:=Podrizeni[Sef]; Podrizeni[Sef]:=i; end; end; end; procedure Rekurze(KdeJsem:integer;Hloubka:Integer;PocetPodstromu:PVelkeCislo); var Podstromy:integer; PocetPodstromuPodrizeneho:PVelkeCislo; begin if Length(PomocnaCisla)=Hloubka then begin {Máme dost pomocných čísel?} {Hloubka vzrůstá o 1, takže případ Length < Hloubka nemůze nastat} SetLength(PomocnaCisla,Hloubka+1); new(PomocnaCisla[Hloubka],Nove(DelkaCisel)); end; PocetPodstromuPodrizeneho:=PomocnaCisla[Hloubka]; PocetPodstromu^.NastavHodnotu(1); Podstromy:=Podrizeni[KdeJsem]; while Podstromy <> -1 do begin {Projdeme všechny podřízené a podíváme se, kolika způsoby můžeme vytvořit podskupinku} Rekurze(Podstromy,Hloubka+1,PocetPodstromuPodrizeneho); PocetPodstromuPodrizeneho^.Pricti(Jednicka); PocetPodstromu^.Vynasob(PocetPodstromuPodrizeneho); Podstromy:=Spolupracovnici[Podstromy]; end; PocetSkupin^.Pricti(PocetPodstromu); {Nakonec přičteme počet skupin, kde já jsem šéf, k celkovému počtu možností} end; begin Nacti; {Načtení vstupu} DelkaCisel:=floor(n*Ln10_2+Epsilon)+1; {Minimální potřebná délka} Exponent2:=ceil(ln(DelkaCisel)/Ln2+Epsilon); {Reálná délka bude 2^Exponent2, chceme mocninu dvojky} DelkaCisel:=1 shl Exponent2; {Počet cifer čísel} KorenJednicky:=Umocni(Koren26,1 shl (26-Exponent2),Modulus); {Exponent2-tý kořen jedničky} InverzeKorene:=Umocni(KorenJednicky,Modulus-2,Modulus); {Inverzní prvek k uvažovanému kořeni} InverzePoctuCifer:=Umocni(DelkaCisel,Modulus-2,Modulus); {Inverzní prvek k počtu cifer} {Obojí počítáno z malé Fermatovy věty} new(PocetSkupin,Nove(DelkaCisel)); {Inicializace proměnných} new(PocetPodstromu,Nove(DelkaCisel)); new(Jednicka,Nove(DelkaCisel)); PocetSkupin^.NastavHodnotu(0); Jednicka^.NastavHodnotu(1); Rekurze(VelkySef,0,PocetPodstromu); {Vlastní výpočet} PocetSkupin^.Vypis; {Výpis výsledku} for n:=0 to Length(PomocnaCisla)-1 do dispose(PomocnaCisla[n],Zrus); {A uklizení paměti} dispose(PocetSkupin,Zrus); dispose(PocetPodstromu,Zrus); end.