Numărul de partiții ale unui număr natural
La mulți ani 2020! Acum câteva zile (în deceniul trecut) am dat o simulare pe InfoArena la care a picat problema Crescător2. Problema cerea numărul de șiruri crescătoare cu suma elementelor mai mică sau egală cu Asta înseamnă practic numărul de partiții neordonate ale numerelor naturale nenule mai mici sau egale cu În timpul simulării am luat 40 de puncte cu o dinamică în însă upsolving-ul mi-a luat câteva ore bune, pentru că soluția oficială folosea un truc legat de iar mie nu-mi plac trucurile legate de radical Așa că am făcut research pe net pentru a găsi (și mai ales a înțelege) o recurență liniară, research în care am învățat câte ceva despre matematici discrete, funcții generatoare și numere pentagonale. În acest articol voi împărtăși cu voi ce am învățat
Ce sunt partițiile unui număr natural?
Numim partiție a lui o secvență de numere naturale nenule cu proprietatea că Dacă secvența este ordonată, partiția este ordonată, iar în caz contrar, neordonată.
De exemplu, partițiile ordonate ale lui sunt:
Însă, partițiile neordonate ale lui sunt:
Așadar, partițiile ordonate și sunt diferite, pe când partițiile neordonate și sunt egale.
Prin convenție, numărul de partiții ale lui este Convenția este justificată, deoarece putem considera că mulțimea vidă este o partiție a lui suma elementelor ei fiind
Numărul de partiții ordonate
Problema asta e partea simplă a articolului, pentru că s-a dovedit a fi mult mai ușor să numeri partițiile ordonate decât pe cele neordonate. Să vedem mai întâi cum putem număra partițiile în funcție de lungimea lor. Fie numărul de partiții ordonate ale lui de lungime Pentru a ne ușura munca, vom reduce problema la una mai ușor de abordat:
Să se determine numărul modurilor de a împărți un șir de lungime în secvențe de lungimi nenule. De exemplu, șirul
xxxxx
poate fi împărțit în secvențe astfel:
x x xxx
x xx xx
x xxx x
xx x xx
xx xx x
xxx x x
Este clar că o astfel de împărțire a unui șir este de fapt o partiție a lui în care lungimea fiecărei secvențe reprezintă valoarea unui element din partiție.
Practic, trebuie să găsim numărul de moduri de a alege cele puncte de split, adică acele poziții pe care se termină fiecare secvență, mai puțin ultima (pentru că ea are poziția de sfârșit fixată). Aceste poziții iau valori din mulțimea Cum ele trebuie să fie distincte, se observă ușor că soluția e dată de
Acum, dacă vrem să calculăm numărul total de partiții ordonate ale lui nu avem decât să însumăm niște combinări. Mai exact, dacă notăm cu numărul tuturor partițiilor ordonate ale lui obținem:
Numărul de partiții neordonate de lungime dată
Păstrăm notațiile precedente, doar că de data asta se vor referi la partiții neordonate. Din nou, vom număra mai întâi partițiile după lungimea lor. Pentru a calcula vom folosi programare dinamică astfel:
Se observă că putem obține o partiție de lungime a lui în două moduri. Fie luăm o partiție de lungime a lui și adăugăm elementul la începutul ei, fie luăm o partiție de lungime a lui și incrementăm toate elementele acesteia. De exemplu, partiția se obține adăugându-l pe la începutul lui pe când se obține incrementând elementele lui Astfel, orice partiție se poate obține pornind de la mulțimea vidă, aplicând asupra acesteia, pe rând, mai multe operații de aceste două feluri. De pildă, pentru a-l obține pe procedăm astfel:
Așadar, recurența dinamicii noastre este următoarea:
Complexitatea pentru calcularea lui este, evident, Nu putem optimiza nimic, și probabil nu există o soluție mai bună. Putem folosi această dinamică și pentru a calcula
Însă, pentru cerința asta există o soluție mai bună decât cea în ce se bazează pe recurența liniară despre care vorbeam la începutul articolului.
Numărul total de partiții neordonate
În primul rând, trebuie să definim noțiunea de funcție generatoare. Funcția generatoare a unui șir este o funcție ce poate fi scrisă ca o serie de forma:
O serie este un șir infinit între elementele căruia se pune semnul
Vom demonstra mai întâi că următoarea funcție este funcția generatoare a șirului
Dacă desfacem parantezele, înainte să grupăm termenii asemenea, fiecare coeficient al lui va fi practic o partiție a lui De ce? Ei bine, exponentul provine dintr-o expresie de forma Aici, reprezintă practic frecvența lui în cadrul partiției. De exemplu, este partiția Deci, coeficientul lui în seria noastră va fi într-adevăr
Aici începe partea interesantă. Acum trebuie să calculăm eficient coeficienții seriei de mai sus. Observăm că fiecare paranteză este suma unei progresii geometrice de rație Evident, dacă progresia e divergentă. Dar dacă nu, aceasta converge la Înlocuim seriile cu limitele lor și obținem:
De unde:
Aici aplicăm Teorema numerelor pentagonale, formulată de Euler, care ne spune că:
Semnele alternează din în iar exponenții sunt dați de șirul numerelor pentagonale generalizate, adică numerele de forma unde ia pe rând valorile
Ne întoarcem la ecuația noastră și obținem:
Egalăm coeficienții termenilor de grad egal din cele două polinoame și obținem în sfârșit recurența de care avem nevoie:
Implementare
Mai jos aveți sursa mea la problema Crescător2 de pe InfoArena, care, după cum am spus și la început, cere determinarea sumei Mai întâi am precalculat numerele pentagonale până la iar apoi am calculat dinamica mergând la fiecare pas până la cel mai apropiat număr pentagonal de
#include <bits/stdc++.h>using namespace std;
ifstream fin("crescator2.in");ofstream fout("crescator2.out");
const int MOD = 700001;
inline void add(int& x, int y) { x += y; if (x >= MOD) x -= MOD; }inline void sub(int& x, int y) { x -= y; if (x < 0) x += MOD; }
int main() { int n; fin >> n; vector<int> pent(n); for (int i = 0, j = 1; i < n; i++, j = -j + (j <= 0)) pent[i] = (3 * j * j - j) / 2;
vector<int> dp(n + 1); dp[0] = 1; int sol = 0; for (int i = 1; i <= n; i++) { for (int j = 0; pent[j] <= i; j++) if (j & 2) sub(dp[i], dp[i - pent[j]]); else add(dp[i], dp[i - pent[j]]); add(sol, dp[i]); } fout << sol << '\n'; return 0;}
Complexitate
Complexitatea soluției este pentru că numărul pentagonal maxim până la care se iterează la fiecare pas este aproximativ Asta se înmulțește cu deoarece pentru fiecare luăm în considerare și și Așadar, constanta din spatele complexității este de aproximativ
Sfârșit! Dacă aveți vreo întrebare despre partițiile unui număr natural, o puteți lăsa mai jos, într-un comentariu