• 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;
    }
    
  • 相关阅读:
    2.1.1 Speed Limit
    2.1.2 骑自行车的最短时间
    1.3.1提高实数精度的范例
    1.2.2一个数可以有多少种用连续素数之和表示
    求二倍关系的个数 1.2.1
    求平均值
    原生JS 购物车及购物页面的cookie使用
    基于Jquery的商城商品图片的放大镜效果(非组件)
    商城商品购买数量增减的完美JS效果
    弹性布局各种坑爹兼容
  • 原文地址:https://www.cnblogs.com/mangoyang/p/9374747.html
Copyright © 2020-2023  润新知