JAG 2012春コンテスト E - Minimum Spanning Tree

問題

n頂点m辺からなる重みつき無向グラフが与えられる。
これらのうち、i番目の辺(s[i], t[i])を使わないで作った最小全域木の重みを
全てのiについて求めよ。

制約条件

n≦100000
m≦200000
重みは0以上10^6以下の整数

方針

まずは制限のない最小全域木を作る。
作れない場合は-1を返す。


求める最小全域木は、制限のない最小全域木について
i番目の辺が含まれてないなら、制限の最小全域木と同じ、
含まれるなら、その辺を削除して、分かれた二成分のうち、それぞれをつなぐ、
最も重みの小さい辺を、二成分に加えたもの。


これを効率的に求めたい。
頂点vを根とする部分木について、そこから部分木の外へ出るエッジの集合をE[v]とする。
これのうち重み最小のものの重みがわかれば、vとvの親をつなぐエッジを切断したときに、最小全域木を作り直すコストがわかる。


これを、根から順に統合していけば、全てのクエリに対して高速に最小の重みが求まる。
普通に統合するとO(nm)がかかりそうだが、
小さいほうの要素を大きいほうの要素に入れるとやれば、O(mlogm)回の統合だけですむ。

ソースコード

const int MX = 200000;
int n, m, a[MX], b[MX], w[MX];
vector<vector<pi> > e;

vi ord;
int parent[MX], pid[MX];

int euler[MX], sz, s[MX], t[MX];
int of[MX], use[MX];
ll cost, ans[MX];
set<pi> we[MX];

int p[MX];
int root(int x){
	if(x == p[x]) return x;
	return p[x] = root(p[x]);
}

void rec(int c, int p){
	vi C, P, I;
	int i;
	C.pb(c); P.pb(p);
	ONE:
	c = C.back();
	p = P.back();
	
	ord.pb(c);
	euler[sz] = c;
	s[c] = sz++;
	
	for(i = 0; i < e[c].size(); i++) if(e[c][i].first != p){
		C.pb(e[c][i].first); P.pb(c); I.pb(i);
		
		pid[e[c][i].first] = e[c][i].second;
		parent[e[c][i].first] = c;
		
		goto ONE;
		TWO:
		c = C.back(); p = P.back(); i = I.back(); I.pop_back();
	}
	euler[sz] = c;
	t[c] = sz++;
	C.pop_back(); P.pop_back();
	if(C.size()) goto TWO;
}
void rec2(int c, int p, int eid){
	if(c == p) eid = -1;
	
	priority_queue<pi> q;
	rep(i, e[c].size()) if(e[c][i].first != p){
		q.push(mp(-we[of[e[c][i].first]].size(), e[c][i].first));
	}
	q.push(mp(we[of[c]].size(), c));
	while(q.size() > 1){
		int a = q.top().second; q.pop();
		int b = q.top().second; q.pop();
		
		each(i, we[of[a]]) we[of[b]].insert(*i);
		q.push(mp(-we[of[b]].size(), b));
	}
	of[c] = of[q.top().second];
	
	while(we[of[c]].size() && s[c] <= we[of[c]].begin()->second && we[of[c]].begin()->second <= t[c])
	we[of[c]].erase(we[of[c]].begin());
	
	if(eid >= 0){
		ans[eid] = we[of[c]].empty() ? -1 : cost - w[eid] + we[of[c]].begin()->first;
	}
}

int main(){
	scanf("%d%d", &n, &m);
	vector<pi> es;
	rep(i, m){
		scanf("%d%d%d", a + i, b + i, w + i);
		a[i]--; b[i]--;
		es.pb(mp(w[i], i));
	}
	
	rep(i, n) p[i] = of[i] = i;
	e.resize(n);
	sort(all(es));
	int cmp = n;
	
	rep(i, m){
		int id = es[i].second, x = a[id], y = b[id];
		if(root(x) == root(y)) continue;
		e[x].pb(mp(y, id));
		e[y].pb(mp(x, id));
		
		cost += es[i].first;
		use[id] = 1;
		p[root(x)] = root(y);
		cmp--;
	}
	if(cmp != 1){
		rep(i, m) puts("-1");
		return 0;
	}
	
	rec(0, 0);
	rep(i, m) if(!use[i]){
		we[a[i]].insert(mp(w[i], s[b[i]]));
		we[b[i]].insert(mp(w[i], s[a[i]]));
	}
	
	rep(i, m) ans[i] = cost;
	for(int i = ord.size() - 1; i >= 0; i--){
		rec2(ord[i], parent[ord[i]], pid[ord[i]]);
	}
	rep(i, m) printf("%lld\n", ans[i]);
	
	return 0;
}