CodeForces 160D Edges in MST (tarjan)

题目链接:http://codeforces.com/problemset/problem/160/D

题意:给出一个n个点m条边(无自环无重边)的无向图,求问它的所有最小生成树中:哪些边在所有最小生成树中都出现、那些可能出现、那些都不出现。

思路:首先有一个结论:把一个连通无向图的生成树的边按权值递增排序,称排好序的边权列表为有序边权列表,则任意两棵最小生成树的有序边权列表是相同的。因此,借助Kruskal算法思想,先把所有边按权值按从小到大排序,然后每次对权值相同的边一起处理,如果某条边的两个顶点属于同一集合,则该边一定不存在于任何最小生成树(因为会形成环,而环中的其他边是上一次处理权值更小的边时加进去的)。若不在同一集合中,则在这两个集合间添加一条无向边,那么这些边有可能出现在该图的最小生成树中;再进一步,这些边所形成的的所有连通块中,如果存在桥,那么这条边即是在所有最小生成树都存在的边。桥的寻找可以借助tarjan算法(注意dfs时的fa应该是边的标号而不是点的标号,因为上述连边过程中可能存在并查集的两个集合间连多条边,即两点间有多条边)。


#include<cstdio>
#include<cstring>
#include<string>
#include<cctype>
#include<iostream>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef long long LL;

using namespace std;
typedef pair<int, int> P;
const int maxn = 1e5 + 10;
const int INF = 1e9 + 10;

struct Edge {
  int from, to, cost, id, st;
  Edge() {}
  Edge(int u, int v, int c, int id, int st) : from(u), to(v), cost(c), id(id), st(st) {}
  bool operator < (const Edge &rhs) const {
    return cost < rhs.cost;
  }
}edge[maxn];

vector<P> G[maxn];
int pre[maxn], dfs_clock;

int dfs(int u, int fa_id) {
  int lowu = pre[u] = ++dfs_clock;
  for(int i = 0; i < G[u].size(); i++) {
    int v = G[u][i].first, id = G[u][i].second;
    if(fa_id == id) continue;
    if(!pre[v]) {
      int lowv = dfs(v, id);
      lowu = min(lowu, lowv);
      if(lowv > pre[u]) edge[id].st = 2;
    }
    else lowu = min(lowu, pre[v]);
  }
  return lowu;
}

int fa[maxn], ans;

int find(int x) {
  return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);
}

bool cmp(const Edge& A, const Edge &B) {
  return A.id < B.id;
}

int main() {
  int n, m;
  scanf("%d%d", &n, &m);
  for(int i = 1; i <= n; i++) fa[i] = i;
  for(int i = 0; i < m; i++) {
    int u, v, c;
    scanf("%d%d%d", &u, &v, &c);
    edge[i] = Edge(u, v, c, i+1, 0);
  }
  sort(edge, edge+m);
  for(int i = 0; i < m; i++) {
    int s = i, e = i;
    //处理权值相同的边
    while(e+1 < m && edge[e+1].cost == edge[s].cost) e++;
    for(int j = s; j <= e; j++) {
      int x = find(edge[j].from), y = find(edge[j].to);
      if(x == y) continue;
      G[x].push_back(P(y, j));
      G[y].push_back(P(x, j));
      edge[j].st = 1;
    }

    for(int j = s; j <= e; j++) {
      int x = find(edge[j].from), y = find(edge[j].to);
      if(x == y || pre[x]) continue;
      dfs(x, -1);
    }

    for(int j = s; j <= e; j++) {
      int x = find(edge[j].from), y = find(edge[j].to);
      if(x == y) continue;
      G[x].clear(); G[y].clear();
      pre[x] = pre[y] = 0;
      fa[x] = y;
    }
    i = e;
  }

  sort(edge, edge+m, cmp);
  for(int i = 0; i < m; i++) {
    if(edge[i].st == 0) printf("none\n");
    else if(edge[i].st == 1) printf("at least one\n");
    else printf("any\n");
  }
  return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值