牛客练习赛32 D Tarjan无向图求桥+并查集维护

题目描述:

小p和他的朋友约定好去游乐场游玩,但是他们到了游乐场后却互相找不到对方了。
游乐场可以看做是一张n个点,m条道路的图,每条道路有边权wi,表示第一次经过该道路时的花费(第二次及以后经过时花费为0)。
现在,小p要去找他的朋友,但他的朋友行踪很诡异,小p总是要遍历完这n个点才能找到他,同时小p希望总花费最小。
找到朋友的方案可能不唯一(具体看样例解释),小p想知道在这所有的方案中,有多少条边在每个方案中都会被经过。
 

输入描述:

第一行两个整数n, m. p,分别表示点数,边数,小p的初始位置。
接下来m行,每行两个整数u, v, w表示从u到v有一条无向边,边权为w。

输出描述:

输出一个整数k,表示必须经过的边的数量。

示例1

输入

5 7 1
1 2 3
2 3 7
1 3 5
2 4 2
1 5 3
5 4 3
2 5 3

输出

2

样例解释:


几种可能的方案如下:




可以证明,4 - 2和1 - 3这两条边在所有方案中都被经过。
(以上每种方案的总花费均为13,同时可以证明没有比这更优的策略)

示例2

输入

3 3 1
1 2 1
1 3 1
2 3 2

输出

2

示例3

输入

3 3 1
1 2 2
2 3 2
1 3 2

输出

0

备注:

保证图联通,保证无自环,保证无重边

链接:https://ac.nowcoder.com/acm/contest/272/D
来源:牛客网

扫描二维码关注公众号,回复: 4335426 查看本文章

简单来说就是寻找有几条边一定在最小生成树上

将路径按照权值排序后,按照权值由小到大的顺序将点增加到最小生成树中,使用并查集维护图的连通性

如果路径权值相等,路径两个端点已经在连通图中,那么不用添加这条边

如果两个端点不在连通图中,那么将边添加到新图中,然后使用Tarjan算法求解出无向图中的桥,即一定存在于最小生成树中的边

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;
typedef long long ll;
const int maxn = 200100;
int n,m,s;
vector<pair<int,int> > v[maxn];      //v[x] 表示以x开始的节点,pair.first表示边到达的节点,pair.second表示边的编号
struct Edge
{
    int u,v,w,id;
    bool operator<(const Edge&a)const
    {
        return w < a.w;
    }
}edge[maxn];
int dfn[maxn],low[maxn],k,per[maxn],ans[maxn];

void tarjan(int x,int fa)          //Tarjan求解图中的桥
{
    dfn[x] = low[x] = ++k;
    for(int i = 0,to,id;i < v[x].size();i ++)
    {
        if((id = v[x][i].second) == fa) continue;        //路径返回了回去,不用考虑
        to = v[x][i].first;
        if(!dfn[to]){
            tarjan(to,id);low[x] = min(low[x],low[to]);
            if(low[to] > dfn[x]) ans[id] = 1,cout<<edge[id].u<<" "<<edge[id].v<<endl;;
        }
        else
            low[x] = min(dfn[to],low[x]);
    }
}
int find(int x)
{
    return x == per[x]?per[x]:per[x] = find(per[x]);
}

int main()
{
    int x,y,z;
    scanf("%d%d%d",&n,&m,&s);
    for(int i = 1; i <= n;i ++) per[i] = i;
    for(int i = 1;i <= m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        edge[i] = (Edge){x,y,z,i};
    }
    sort(edge+1,edge+1+m);
    int nxt;
    for(int i = 1;i <= m;i = nxt + 1)
    {
        nxt = i+1;
        for(; edge[i].w == edge[nxt].w; nxt++);
        nxt--;             //[i-nxt]为路径权值相等的区间范围
        for(int j = i;j <= nxt;j ++)       //遍历这个区间
        {
            x = find(edge[j].u),y = find(edge[j].v);
            if(x == y) continue;               //如果已经连通,则不需要考虑这条边
            v[x].push_back(make_pair(y,j));    //否则将这条边添加到图中
            v[y].push_back(make_pair(x,j));
        }
        for(int j = i;j <= nxt;j ++)
        {
            x = find(edge[j].u),y = find(edge[j].v);
            if(x == y || dfn[x]) continue;
            tarjan(x,-1);
        }
        for(int j = i;j <= nxt;j ++)
        {
            x = find(edge[j].u),y = find(edge[j].v);
            if(x == y) continue;
            v[x].clear();v[y].clear();
            dfn[x] = 0;dfn[y] = 0;
            per[x] = y;
        }
    }
    int tot = 0;
    for(int i = 1;i <= m;i ++) tot += ans[i];
    printf("%d\n",tot);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/li1615882553/article/details/84709922