#include #include #include using namespace std; // struct pro reprezentaci hran; u a v značí vrcholy, které hrana spojuje struct hrana { int u; int v; int odolnost; hrana() {} hrana(int u, int v, int o) { this->u = u; this->v = v; this->odolnost = o; } //operator pro sestupné třídění podle odolnosti bool operator < (const hrana& h) const { return (odolnost > h.odolnost); } }; struct dfu_vrchol { bool root; //je vrchol kořenem své komponenty - a představuje tak samotný index komponenty? int rank; int parent; //pokud není root, parent obsahuje index předka v rámci posloupnosti 'vrcholy'; je-li root, parent je přímo index komponenty dfu_vrchol() {} //počáteční inicializace - každý vrchol má vlastní komponentu (v níž je kořenem) dfu_vrchol(int index) { root = true; rank = 0; parent = index; } }; //prvky posloupnosti P struct dvojice { int odolnost; int pocetKomponent; dvojice() {} dvojice(int o, int p) { odolnost = o; pocetKomponent = p; } }; vector hrany; vector vrcholy; vector P; //spolu s binárním vyhledáváním naše výsledná datová struktura //dfu operace (až do řádku 72): //dostane index vrcholu int dfu_find(int vrchol) { if (vrcholy[vrchol].root) return vrcholy[vrchol].parent; int komponenta = dfu_find(vrcholy[vrchol].parent); vrcholy[vrchol].parent = komponenta; //path compression return komponenta; } /* * obecně bychom asi chtěli union pro obecné vrcholy * pro náš případ postačí union kořenů * (a obecný případ by stejně vyžadoval použití find) */ void dfu_union(int root1, int root2) { if (vrcholy[root1].rank >= vrcholy[root2].rank) { vrcholy[root2].root = false; vrcholy[root2].parent = root1; if (vrcholy[root1].rank == vrcholy[root2].rank) { vrcholy[root1].rank++; } } else { vrcholy[root1].root = false; vrcholy[root1].parent = root2; } } // PŘEDPOKLÁDANÝ FORMÁT VSTUPU: // na prvním řádku čísla N, M, T představující počet družic, počet propojujících komunikačních kanálů a počet nakonec získaných dotazů // na dalších M řádcích vždy čísla u, v, o, tj. družice indexů u a v, které jsou spojeny nějakým komunikačním kanálem a o jako odolnost vůči rušení // na dalších T řádcích jediné číslo Q představující dotazovanou intenzitu rušení // všechna čísla na vstupu jsou nezáporná celá a vejdou se do intu // očekává se, že na standardní výstup na samostatné řádky vypíšeme odpovědi na dotazy v takovém pořadí, v jakém přišly na vstupu int main() { int N, M, T; cin >> N >> M >> T; //počáteční nastavení DFU a proměnné k int k = N; vrcholy.resize(N); for (int i = 0; i < N; i++) { vrcholy[i] = dfu_vrchol(i); } //načtení hran: int u, v, o; hrany.resize(M); for (int i = 0; i < M; i++) { cin >> u >> v >> o; hrany[i] = hrana(u, v, o); } //vytvoření pole P: sort(hrany.begin(), hrany.end()); //pro jednoduchost nebudeme počítat H // (asymptoticky to vyjde nastejno, protože vector::push_back je amortizovaně konstantní) // (pro více informací o amortizaci viz např. Průvodce labyrintem algoritmů - kapitolku o amortizaci - hned první příklad tam je 'nafukovací pole' a vector je implementací této struktury) for (int i = 0; i < M; i++) { do { int root1 = dfu_find(hrany[i].u); int root2 = dfu_find(hrany[i].v); if (root1 != root2) { dfu_union(root1, root2); k--; } i++; } while (i < M && hrany[i].odolnost == hrany[i-1].odolnost); i--; P.push_back(dvojice(hrany[i].odolnost, k)); } //teď už odpovídání na dotazy: int Q; for (int i = 0; i < T; i++) { cin >> Q; if (Q > P[0].odolnost) { cout << N; continue; } //binární vyhledávání správného prvku P: int l = 0; int r = P.size() - 1; int hledanyIndex; while (l <= r) { int m = (l + r) / 2; if (P[m].odolnost >= Q) { hledanyIndex = m; l = m + 1; } else r = m - 1; } cout << P[hledanyIndex].pocetKomponent << endl; } return 0; }