Codeforces 160 D. Edges in MST

## [$>Codeforces space 160 D. Edges in MST<$](http://codeforces.com/problemset/problem/160/D)

题目大意 : 给出一张带权无向图,求对于这张图上的每一条边,其是必然存在于每一种最小生成树中,还是至少存在于一种最小生成树中,还是一定不会存在于最小生成树中

$2 leq n leq 10^5, n - 1 leq m leq min(10^5, frac{n(n-1)}{2}) $

解题思路 :

考虑 (kruskal) 算法的本质,每次加进一条边到生成树中如果形成环就替换掉环上的一条最大的边 (如果比它大)

考虑最小生成树不同的原因本质就是替换掉的最大边的边权可以等于它,这样最小生成树的边就会不同

那么可以对于同种颜色的边进行考虑,先求出小于该种边权的边都加进去后的森林

如果说一条边会和这个森林形成环,说明环上的最大值小于它,那么这条边一定不存在于最小生成树中

考虑剩下的一条边如果要存在与所有最小生成树中,那么所有同权的边都不能换掉它

也就是说单独把存在与最小生成树中的同权的边组成一张无向图,这条边不在任意一个环上

也就是说,这条边是一条割边,所以可以通过一遍 (Tarjan) 确定一条在最小生成树中的边是否存在于所有最小生成树中

注意 (Tarjan) 求割边的算法不允许有重边存在,所以在建图之前要把重边判掉,然后把由某条重边组成的割边日掉

考虑每一条边只会被加进做 (Tarjan) 的图中一次,所以复杂度是 (O(mlogm))


/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int f = 0, ch = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
#define N (500005)
map<pair<int, int>, int> mp;
int Ans[N], fa[N], n, m; 
struct Point{ int x, y, z, id; } e[N];
inline bool cmp(Point A, Point B){ 
	return A.z < B.z; 
}
inline int ask(int x){ 
	return x == fa[x] ? x : fa[x] = ask(fa[x]); 
}
namespace Graph{
	int dfn[N], low[N], Index; 
	struct Edge{ int x, id; }; vector<Edge> g[N];
	map<int, int> mp; vector<int> s;
	inline void Clear(){
		for(int i = 0; i < s.size(); i++) 
			dfn[s[i]] = low[s[i]] = 0, g[s[i]].clear();
		s.clear(), mp.clear(), Index = 0;
	}
	inline void Add(int x, int y, int id){
		g[x].push_back((Edge){y, id});
		g[y].push_back((Edge){x, id});
		if(!mp[x]) s.push_back(x), mp[x] = 1;
		if(!mp[y]) s.push_back(y), mp[y] = 1;
	}
	inline void dfs(int u, int fa){
		dfn[u] = low[u] = ++Index;
		for(int i = 0; i < g[u].size(); i++){
			int v = g[u][i].x; 
			if(!dfn[v]){
				dfs(v, u), low[u] = Min(low[u], low[v]);
				if(low[v] > dfn[u]) Ans[g[u][i].id] = 2;
			}
			else if(v != fa) low[u] = Min(low[u], dfn[v]);
		}
	}
	inline void solve(){
		for(int i = 0; i < s.size(); i++) if(!dfn[s[i]]) dfs(s[i], 0);
	}
}
inline void solve(int l, int r){
	Graph::Clear(), mp.clear();
	for(int i = l; i <= r; i++){
		int p = ask(e[i].x), q = ask(e[i].y);
		if(p == q) Ans[e[i].id] = 1; 
		else{
			pair<int, int> now = make_pair(min(p, q), max(p, q));
			if(++mp[now] == 1) Graph::Add(p, q, e[i].id);
		}
	}
	Graph::solve();
	for(int i = l; i <= r; i++){
		int p = ask(e[i].x), q = ask(e[i].y);
		if(p == q) continue;
		if(mp[make_pair(min(p, q), max(p, q))] > 1) Ans[e[i].id] = 0;
	}
	for(int i = l; i <= r; i++){
		int p = ask(e[i].x), q = ask(e[i].y);
		if(p != q) fa[p] = q;
	}
}
int main(){
	read(n), read(m);
	for(int i = 1; i <= n; i++) fa[i] = i;
	for(int i = 1, x, y, z; i <= m; i++){
		read(x), read(y), read(z);
		e[i] = (Point){x, y, z, i};
	}
	sort(e + 1, e + m + 1, cmp), e[0].z = e[m+1].z = -1;
	for(int i = 1, l = 1; i <= m; i++){
		if(e[i].z != e[i-1].z) l = i;
		if(e[i].z != e[i+1].z) solve(l, i);
	}
	for(int i = 1; i <= m; i++){
		if(Ans[i] == 0) puts("at least one");
		if(Ans[i] == 1) puts("none");
		if(Ans[i] == 2) puts("any");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/mangoyang/p/9374747.html