De ce contează?
Dai o petrecere și ai 3 prieteni pe listă. Pe fiecare îl poți invita sau nu — sunt două variante pentru fiecare, independent de ceilalți. Câte grupuri diferite de invitați poți forma? Inclusiv varianta „nu invit pe nimeni” și varianta „îi invit pe toți”. Numărarea acestor grupuri este exact numărarea submulțimilor.
De ce sunt 2^n submulțimi
O submulțime se obține alegând, pentru fiecare element, dacă îl iei sau nu.
Gândește-te la cele n elemente la rând. Pentru primul element ai 2 alegeri
(în submulțime sau afară). Pentru al doilea, tot 2 alegeri, independent de primul.
Și așa mai departe, până la al n-lea.
Cum alegerile sunt independente, înmulțești numărul de variante:
2 · 2 · 2 · ... · 2 (de n ori) = 2^nFiecare combinație de decizii „în/afară” dă o submulțime distinctă, și fiecare
submulțime corespunde unei singure combinații de decizii. Deci numărul total este
fix 2^n. Acest total include două cazuri speciale care se uită ușor:
- mulțimea vidă — ai răspuns „afară” la toate elementele;
- mulțimea totală — ai răspuns „în” la toate elementele.
Exemplu pas cu pas: n = 3
Fie mulțimea {a, b, c}. Avem 2^3 = 8 submulțimi. Le enumerăm pe toate,
ordonate după câte elemente conțin:
{}— mulțimea vidă (0 elemente){a},{b},{c}— submulțimile cu 1 element{a, b},{a, c},{b, c}— submulțimile cu 2 elemente{a, b, c}— mulțimea totală (3 elemente)
Numără: 1 + 3 + 3 + 1 = 8. Se potrivește cu 2^3. Observă și că numărul de
submulțimi de o anumită mărime k este o combinare C(n, k): aici
C(3,0) = 1, C(3,1) = 3, C(3,2) = 3, C(3,3) = 1. Dacă vrei doar
submulțimile de o mărime fixă, folosești combinări — vezi lecția de combinări
din acest capitol. Suma tuturor combinărilor dă, firește, tot 2^n.
Implementare C++
Calculul direct cu long long merge doar pentru n mic. Apoi numerele explodează,
de aceea convenția capitolului este să lucrezi modulo un prim,
de regulă MOD = 1000000007.
#include <iostream>
using namespace std;
const long long MOD = 1000000007;
// 2^n exact, fara modulo - corect doar pentru n mic (n <= 62)
long long submultimiExact(int n) {
long long total = 1; // pornim de la 2^0 = 1
for (int i = 0; i < n; i++) {
total = total * 2; // dublam de n ori
}
return total;
}
// 2^n modulo MOD - sigur pentru orice n, fara overflow
long long submultimiModulo(int n) {
long long total = 1;
for (int i = 0; i < n; i++) {
total = (total * 2) % MOD; // reducem la fiecare pas
}
return total;
}
int main() {
int n = 3;
cout << submultimiExact(n) << "\n"; // 8
cout << submultimiModulo(n) << "\n"; // 8 (n mic, nu se schimba)
int m = 100;
// submultimiExact(100) ar da overflow; folosim varianta modulo
cout << submultimiModulo(m) << "\n"; // 2^100 mod 1000000007
return 0;
}Reducerea cu % MOD la fiecare înmulțire ține valoarea mereu sub MOD, deci
produsul total * 2 nu poate depăși long long. Atât timp cât te interesează
restul modulo MOD, rezultatul rămâne corect.
Complexitate
| Variantă | Timp | Spațiu | Limită practică |
|---|---|---|---|
submultimiExact (long long) | O(n) | O(1) | n <= 62, altfel overflow |
submultimiModulo | O(n) | O(1) | orice n |
| ridicare la putere rapidă | O(log n) | O(1) | orice n, mai rapid |
Bucla face n înmulțiri, deci e O(n). Dacă n e uriaș (de exemplu citit ca
număr mare), folosești ridicarea la putere în timp logaritmic (exponentiere
rapidă) pentru a calcula 2^n mod MOD în O(log n) pași.
- Uiți mulțimea vidă:
2^ninclude deja submulțimea goală. Dacă numeri „doar submulțimile nevide”, atunci răspunsul este2^n - 1, nu2^n. 2^nfără long long / modulo: cuintdepășești lan = 31, culong longlan = 63. Pentrunmare trebuie obligatoriu modulo, conform convenției capitolului (MOD = 1000000007).- Confuzi „toate submulțimile” cu „submulțimi de mărime k”: toate sunt
2^n; cele de mărime fixăksuntC(n, k). Sunt întrebări diferite. 2^n - 1calculat greșit modulo: dacă scoți mulțimea vidă după reducere, fă(2^n mod MOD - 1 + MOD) % MOD, ca să nu obții un rezultat negativ când restul a ajuns 0.
| decizie | 1 | 0 | 1 |
a | b | c |
Recapitulare
- O mulțime cu
nelemente are2^nsubmulțimi: fiecare element intră sau nu, produs denfactori de 2; totalul include mulțimea vidă și mulțimea totală. - Submulțimile de mărime exact
kse numără cu combinăriC(n, k), iar suma tuturor combinărilor redă2^n. 2^ncrește exponențial și depășește repedelong long, deci la concurs calculezi modulo un prim (MOD = 1000000007), reducând la fiecare înmulțire.