UTPC2013 F 魔法の糸
問題
日本語なので本文参照(http://utpc2013.contest.atcoder.jp/tasks/utpc2013_06)
n頂点からなる無向グラフが与えられる。
最初全ての頂点は自分自身だけかならるグループで、クエリによりグループ同士をマージしていく。
グループA, Bをマージした後、新しくできたグループをCとする。
C内部の頂点同士を接続する辺だけを使ってCを連結にするコストを求めよ、というクエリが沢山くるので答えよ。
一度マージしたグループは分かれることはない。
マージのたびに連結にかかるコストはリセットして求める。
制約条件
n≦2000
m≦200000
クエリ<n
方針
グループの中の辺は、グループで最小全域木を使うのに使う辺以外は捨ててしまってよい。
(余分な辺を使って後々別のグループとマージしたときに全域木を作るのに有利になるかというと、ならないので)
グループA, Bをマージするときは、
Aから出てBに入るorBから出てAに入る辺だけを見ればいい。
外に出てる辺が少ないグループを多いグループに統合すれば、
枝のマージ回数があまり多くならなくて済む。
(最悪計算量がいくつなのかよくわかってない)
で、マージした後、全域木を作って、
使わなかった辺は全部捨てる、というのを繰り返せばいい。
ソースコード
#define F first #define S second int n, m, p[2000]; map<pi, int> inner[2000], outer[2000]; int root(int x){ if(p[x] < 0) return x; return p[x] = root(p[x]); } int p2[2000]; int root2(int x){ if(p2[x] == x) return x; return p2[x] = root2(p2[x]); } int calc(const vi & v, map<pi, int> &g){ vector<pair<int, pi> > es; vector<pi> del; each(i, g) es.pb(mp(i->S, i->F)); sort(all(es)); int res = 0, cnt = 0; rep(i, 2000) p2[i] = i; rep(i, es.size()){ int a = es[i].S.F, b = es[i].S.S; //cerr<<"e:: "<<a<<" "<<b<<endl; a = root2(a); b = root2(b); if(a == b){ del.pb(es[i].S); continue; } p2[a] = b; cnt++; res += es[i].F; } rep(i, del.size()) g.erase(del[i]); return cnt == v.size() - 1 ? res : -1; } int main(){ cin >> n >> m; rep(i, n) p[i] = -1; rep(i, m){ int a, b, c; cin >> a >> b >> c; outer[a][mp(a, b)] = c; outer[b][mp(b, a)] = c; } int qs; cin >> qs; while(qs--){ int a, b; cin >> a >> b; a = root(a); b = root(b); if(outer[a].size() > outer[b].size()) swap(a, b); //cerr<<"a: "<<a<<" b: "<<b<<endl; each(i, outer[a]){ if(root(i->F.S) == b){ int x = i->F.F, y = i->F.S; if(x > y) swap(x, y); //cerr<<"x: "<<x<<" y: "<<y<<endl; inner[b][mp(x, y)] = i->S; } else{ outer[b][i->F] = i->S; } } each(i, inner[a]) inner[b][i->F] = i->S; p[a] = b; vi v; rep(i, n) if(root(i) == b) v.pb(i); int dist = calc(v, inner[b]); if(dist < 0) cout << "IMPOSSIBLE" << endl; else cout << dist << endl; } return 0; }