/* * Rychlé řešení 32-5-5 od Medvěda * * - minimální kostru hledáme Kruskalem * - Union-Find s Union by rank * - cestičková maxima v čase O(log n) pomocí heavy-light dekompozice */ #include #include #include #include #include using namespace std; #if 0 #define DEBUG(x) cerr << x #define DEBUG_HLD #else #define DEBUG(x) do { } while (0) #endif /*** Reprezentace grafu ***/ int N; int M; struct vertex; struct hpath; // Hrana struct edge { vertex *u, *v; // Koncové vrcholy int w; // Váha int index; // Pořadové číslo hrany ve vstupu bool in_tree; // Je součástí minimální kostry? edge(vertex *_u, vertex *_v, int _w, int _index) { u = _u, v = _v, w = _w, index = _index; in_tree = false; } bool operator < (edge &than) { return w < than.w; } vertex *other_end(vertex *x) { return (u == x) ? v : u; } }; // Vrchol struct vertex { int index; // Pořadové číslo ve vstupu vector edges; // Hrany kostry vedoucí z tohoto vrcholu vertex *parent; // Rodič v kostře edge *parent_edge; // Jaká hrana vede do rodiče int subtree_size; // Počet vrcholů v podstromu zakořeněném v tomto vrcholu vertex *heavy_child; // Do kterého potomka vede těžká hrana hpath *heavy_path; // Na které těžké cestě tento vrchol leží ... int path_pos; // ... a kolikátý shora tam je vertex() { parent = NULL; parent_edge = NULL; heavy_child = NULL; heavy_path = NULL; path_pos = 0; } }; vector vertices; vector edges; /*** Union-Find ***/ struct union_find { vector parent; vector rank; union_find() { parent.resize(N, -1); rank.resize(N, 0); } int find(int u) { while (parent[u] >= 0) u = parent[u]; return u; } void union_roots(int u, int v) { assert(parent[u] == -1 && parent[v] == -1); if (rank[u] < rank[v]) swap(u, v); else if (rank[u] == rank[v]) rank[u]++; parent[v] = u; } }; /*** Hledání minimální kostry ***/ int64_t mst_weight = 0; // Váha nalezené kostry // Projde nalezenou kostru do hloubky, spočítá rodiče, velikosti podstromů a těžké hrany void build_tree(vertex *u, vertex *parent) { int size = 1; vertex *max_child = NULL; int max_csize = -1; for (auto e: u->edges) { vertex *v = e->other_end(u); if (v != parent) { v->parent = u; v->parent_edge = e; build_tree(v, u); if (v->subtree_size > max_csize) { max_csize = v->subtree_size; max_child = v; } size += v->subtree_size; } } u->subtree_size = size; u->heavy_child = max_child; } void build_mst() { union_find uf; // Setřídíme hrany podle váhy a zkontrolujeme, že váhy jsou unikátní sort(edges.begin(), edges.end()); for (int i=1; iindex); int rv = uf.find(e.v->index); if (ru != rv) { e.in_tree = true; mst_weight += e.w; e.u->edges.push_back(&e); e.v->edges.push_back(&e); uf.union_roots(ru, rv); } } DEBUG("MST weight: " << mst_weight << endl); build_tree(&vertices[0], NULL); } /*** Heavy-light dekompozice ***/ // Jedna hrana na těžké cestě struct path_edge { int w; // Váha edge *e; // Odkaz na hranu kostry path_edge() { // Nedefinovaná hrana, lehčí než všechny reálné w = -1, e = NULL; } path_edge(edge *_e) { e = _e; w = e->w; } // Aktualizace průběžného maxima void max_with(path_edge x) { if (x.w > w) { w = x.w; e = x.e; } } bool operator < (const path_edge with) const { return w < with.w; } }; // Celá těžká cesta struct hpath { int index; // Index nejvyššího vrcholu cesty (jejího vršku) int n; // Délka cesty zaokrouhlená na nejvyšší mocninu 2 vertex *top; // Vršek cesty vector itree; // Intervalový strom v polu: 1..n-1 jsou vnitřní vrcholy, n..2n-1 listy vector prefix_max; // Předpočítaná prefixová maxima vertex *tmp_vertex; // Značky při vyhledávání int tmp_pos; // Vybudování těžké cesty void build(vertex *u) { // Inicializace top = u; index = u->index; tmp_vertex = NULL; tmp_pos = -1; // Projdeme cestu a spočítáme pozice vrcholů na ní int len = -1; for (vertex *uu = u; uu; uu = uu->heavy_child) { uu->heavy_path = this; uu->path_pos = ++len; } // Alokujeme intervalový strom n = 1; while (n < len) n *= 2; itree.resize(2*n, path_edge()); // Do listů intervaláku zaznamenáme hrany cesty int at = n; for (vertex *uu = u; uu->heavy_child; uu = uu->heavy_child) itree[at++] = path_edge(uu->heavy_child->parent_edge); // Dopočítáme maxima ve vnitřních vrcholech intervaláku for (int i = n-1; i > 0; i--) itree[i] = max(itree[2*i], itree[2*i+1]); // Spočítáme prefixová maxima prefix_max.resize(len+1); for (int i=0; i heavy_paths; // Prohledáme kostru do hloubky a postupně vyrobíme všechny těžké cesty void build_heavy_path(vertex *u) { // Těžká cesta z aktuálního vrcholu dolů hpath *hp = &heavy_paths[u->index]; hp->build(u); // Rekurze na všech lehkých hranách odbočujících z těžké cesty for (vertex *uu = u; uu; uu = uu->heavy_child) { for (auto e: uu->edges) { vertex *vv = e->other_end(uu); if (vv != uu->heavy_child && vv != uu->parent) build_heavy_path(vv); } } } // Debugovací funkce pro zobrazení celé dekompozice void show_hld(vertex *u, int indent=0, bool is_heavy_child=false) { for (int i=0; iindex << " (p=" << (u->parent ? u->parent->index : -1) << ", pe=" << (u->parent_edge ? u->parent_edge->index : -1) << ", hc=" << (u->heavy_child ? u->heavy_child->index : -1) << ", hp=" << (u->heavy_path ? u->heavy_path->index : -1) << ", pp=" << u->path_pos << ")"; if (u->heavy_path && !u->path_pos) { cerr << " tree:"; for (int i=0; i < 2 * u->heavy_path->n; i++) cerr << ' ' << u->heavy_path->itree[i].w; } cerr << endl; if (u->heavy_child) show_hld(u->heavy_child, indent+4, true); for (auto e: u->edges) { vertex *v = e->other_end(u); if (v != u->heavy_child && v != u->parent) show_hld(v, indent+4); } } // Vybudování dekompozice se vším všudy void build_hld() { heavy_paths.resize(N); build_heavy_path(&vertices[0]); #ifdef DEBUG_HLD show_hld(&vertices[0]); #endif } /*** Cestičková maxima ***/ // Projdeme všechny těžké cesty od vrcholu u až po cestu stop_at_hp, // spočítáme všechna cestičková maxima (vždy jsou prefixová) a započítáme // též projité lehké hrany. path_edge max_to_hp(vertex *u, hpath *stop_at_hp) { path_edge maxe; hpath *hp; while ((hp = u->heavy_path) != stop_at_hp) { maxe.max_with(hp->range_max(0, u->path_pos)); maxe.max_with(path_edge(hp->top->parent_edge)); u = hp->top->parent; } return maxe; } // Výpočet maxima na cestě mezi vrcholy u,v pomocí HLD edge *path_max(vertex *u, vertex *v) { DEBUG("pmi(" << u->index << ", " << v->index << ")\n"); // Jdeme z u nahoru po těžkých cestách, značíme je a zapisujeme si, // kde jsme se na kterou cestu napojili. U každé značky si pamatujeme, // že jsme vyráželi zrovna z vrcholu u, abychom pak značky nemuseli mazat. vertex *uu = u, *vv = v; while (uu) { hpath *hp = uu->heavy_path; hp->tmp_vertex = u; hp->tmp_pos = uu->path_pos; uu = hp->top->parent; } // Pak stoupáme z v, dokud nenarazíme na už označenou těžkou cestu. hpath *lca_hp = NULL; int lca_upos = -1, lca_vpos = -1; for (;;) { hpath *hp = vv->heavy_path; if (hp->tmp_vertex == u) { lca_hp = hp; lca_upos = hp->tmp_pos; lca_vpos = vv->path_pos; break; } vv = hp->top->parent; assert(vv); } DEBUG("\tLCA hp=" << lca_hp->index << ", upos=" << lca_upos << ", vpos=" << lca_vpos << endl); // Na poslední těžké cestě počítáme obecné intervalové maximum, // na všech ostatních navštívených jen rychlá prefixová maxima. path_edge pmax = lca_hp->range_max(min(lca_upos, lca_vpos), max(lca_upos, lca_vpos)); pmax.max_with(max_to_hp(u, lca_hp)); pmax.max_with(max_to_hp(v, lca_hp)); DEBUG("\tmax: edge " << pmax.e->index << ", weight " << pmax.w << endl); return pmax.e; } /*** Druhá nejlehčí kostra ***/ void find_mst2() { edge *best_te = NULL, *best_ne = NULL; int best_gain = 2000000000; // Procházíme všechny hrany mimo minimální kostru for (int ni=0; niin_tree) continue; // Cestičkové maximum edge *te = path_max(ne->u, ne->v); assert(te->in_tree && te->w < ne->w); // O kolik kostra ztěžkne prohozením? int gain = ne->w - te->w; DEBUG("Replacing " << te->index << " by " << ne->index << ": gain " << gain << endl); if (gain < best_gain) { best_gain = gain; best_te = te; best_ne = ne; } } DEBUG("Best: Replace edge " << best_te->index << " by " << best_ne->index << " with gain " << best_gain << endl); // Vypíšeme výstup cout << mst_weight + best_gain << endl; for (auto &e: edges) { if (e.in_tree && &e != best_te) cout << e.index << " "; } cout << best_ne->index << endl; } /*** Hlavní program ***/ void read_input() { cin >> N >> M; vertices.resize(N); for (int i=0; i> u >> v >> w; assert(u >= 0 && u < N); assert(v >= 0 && v < N); assert(w >= 1 && w <= 1000000000); edges.push_back(edge(&vertices[u], &vertices[v], w, i)); } } int main() { read_input(); build_mst(); build_hld(); find_mst2(); }