Problema partit
Fie PN = numărul de partiții ale lui N.
Dacă setăm valoarea primului element din partiție ca fiind:
1=> Numărul partițiilor rămase estePN-12=> Numărul partițiilor rămase estePN-2- …
K=> Numărul partițiilor rămase estePN-K
Deci, avem:
PN = PN-1 + PN-2 + ... + P1 + P0
Dar:
PN-1 = PN-2 + PN-3 + ... + P1 + P0
Deci:
PN = 2 * PN-1
Deci:
PN = 2N-1 * P1=>PN = 2N-1
Se poate observa că în problemă nu ne interesează partiția N = N, deci PN = 2N-1 - 1.
Soluție #1 (programare dinamică)
Ne vom construi o tabelă:
dp[i]= numărul de partiționări ale număruluii
Pentru a reconstrui soluția ne vom plimba prin valorile tabelei, similar ca la multe alte probleme discutate anterior.
Soluție #2 (directă)
Subpunctul #1
Avem 2N-1 partiții ale lui N. Dintre acestea, 2N-2 încep cu 1, iar celelalte încep cu un număr mai mare decât 1.
Deci, ne putem construi o funcție recursivă care:
- Dacă
K <= 2N-2, putem afișa1și apelăm recursiv pentruN-1. - Altfel
- Partiția începe cu
2dacă se află în prima jumătate din restul partițiilor - Partiția începe cu
3dacă se află în prima jumătate din restul partițiilor (cele care nu încep nici cu1, nici cu2) - …
- Partiția începe cu
Deci funcția va căuta elementul care trebuie pus pe prima poziție, urmând să se apeleze recursiv pentru a extrage rezultatul pentru restul sumei.
Exemplu implementare:
void solve(FILE* fout, int n, int64 k) {
if (n > 62) {
fprintf(fout, "1 ");
solve(fout, n - 1, k);
return;
}
if (n == 0)
return;
int elem;
elem = 1;
while (elem < n && k > (1LL << (n - elem - 1))) {
k -= (1LL << (n - elem - 1));
++elem;
}
fprintf(fout, "%d ", elem);
solve(fout, n - elem, k);
}
Subpunctul #2
Rezolvarea se bazează pe aceleași observații folosite la primul subpunct.
Exemplu implementare:
k = 1;
while (fscanf(fin, "%d", &x) == 1) {
// 2^(n-2) + 2^(n-3) + ... + 2^(n-x-1)
// 2^(n-1)-1 - (2^(n-x)-1)
k += (1LL << (n - 1)) - (1LL << (n - x));
n -= x;
}
fprintf(fout, "%lld\n", k);
