Cod sursa(job #654051)

Utilizator titeltitel popescu titel Data 29 decembrie 2011 14:23:11
Problema Hashuri Scor 100
Compilator cpp Status done
Runda Arhiva educationala Marime 3.55 kb
// Pentru a rezolva eficient in practica problema de fata putem folosi tabele hash (tabele de dispersie). 
// Desi teoretic complexitatea acestei structuri de date ramane O(N) pe cazul cel mai defavorabil,
// in practica operatiile uzuale precum cautarea, adaugarea sau stergerea se realizeaza foarte rapid. 
// Ideea din spatele hashurilor este de a asocia fiecarui element dintr-o multime data o valoare unica, 
// obtinuta printr-o functie de hash H. Valorile functiei H sunt de obicei numere naturale.
// Elementele multimii (care pot fi numere naturale, siruri de caractere, matrici, etc.) se vor numi chei.
// Avantajul asocierilor de tipul cheie-valoare este dat de usurinta cu care putem manipula valorile, 
// in special in cazul in care acestea au dimensiuni mici. Astfel, vom retine o a doua multime (pe care o vom nota cu M),
// care sa contina toate valorile de forma H(i), unde i apartine multimii asupra careia se efectueaza operatiile. 
// Atunci cand adaugam un element i la prima multime trebuie de fapt sa adaugam H(i) la multimea M,
// iar cand stergem un element i sa stergem H(i). Interogarea unui element i se reduce la a afla daca elementul H(i) se gaseste in M. 
// Aceasta abordare functioneaza cand functia H este injectiva, adica din oricare doua chei distincte se obtin valori distincte. 
// Cel mai simplu exemplu este sa alegem H(x) = x si sa retinem multimea M ca un vector boolean, 
// in care pe pozitia i apare true daca si numai daca i este in multime.
// Totusi, pentru aceasta problema nu putem construi o functie H injectiva,
// deoarece in acest caz codomeniul ar contine cel putin 2 miliarde de elemente iar memoria este limitata la 16 MB.
// In acest caz putem considera o functie H neinjectiva, insa trebuie sa tratam aparitia coliziunilor,
// adica acele cazuri in care doua chei distincte furnizeaza aceeasi valoare. Fie functia de hash H(x) = x modulo P,
// unde P este un numar natural, ales de obicei ca fiind un numar prim. Vom retine P liste simplu inlantuite, 
// indexate de la 0 la P-1, unde lista i va retine toate acele chei x din multime care au H(x) = i, sau, altfel spus,
// toate numerele din multime care dau restul i la impartirea cu P. Atunci cand efectuam o operatie de tipul 1,
// vom adauga elementul x la lista corespunzatoare din cele P (si anume la lista H(x)),
// iar pentru o operatie de tipul 2 vom sterge elementul x din lista H(x). Operatia 3 se trateaza similar.
// Se observa ca daca P este comparabil cu N, distributia cheilor se face in practica cat mai uniform, 
// adica cele P liste au lungime mica iar toate cele trei operatii executa putine calcule, programul devenind deci foarte rapid.
// Sursa da 100 de puncte.
#include <fstream>
#include <vector>
#define MOD 666013
using namespace std;
ifstream f("hashuri.in"); ofstream g("hashuri.out");
int N;
vector<int> G[MOD];
inline vector<int>::iterator cauta(int x)
{   int list = x % MOD;
    for (vector<int>::iterator it = G[list].begin(); it != G[list].end(); ++it) if (*it == x) return it;
    return G[list].end();
}
inline void adauga(int x)
{   int list = x % MOD;    
    if (cauta(x) == G[list].end()) G[list].push_back(x);
}
inline void erase_value(int x)
{   int list = x % MOD;
    vector<int>::iterator it = cauta(x);
    if (it != G[list].end()) G[list].erase(it);
}
int main()
{   int op, x;
	f>>N;
    for (; N; --N)
    { 	f>>op>>x;
        if (op == 1) {adauga(x); continue;}        
        if (op == 2) {erase_value(x);; continue;}
		if (cauta(x) == G[x % MOD].end()) g<<"0\n"; else g<<"1\n";
    }
    g.close(); return 0;
}