[POJ3177]Redundant Paths(tarjan求边双)

时间:2022-01-20 18:37:59

题目描述

传送门
题意:给出一个无向图,求最少加多少条边,使这个图成为边双连通图。

题解

将所有的边双连通分量缩点,然后就能形成一个无向无环图(树)。
答案为(树中叶节点的个数+1)/2,也就是说,将叶节点每两个连一条边,使之形成环。
注意tarjan求双连通分量中,重边可以更新,但是双向边的父边不能更新。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 10005

int n,m,dfs_clock,dcc,ans;
int tot,point[N],nxt[N*2],v[N*2];
int dfn[N],low[N],stack[N],tmp,belong[N],du[N];
struct hp{int x,y;}e[N];
bool vis[N];

void add(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void tarjan(int x,int fa)
{
    dfn[x]=low[x]=++dfs_clock;vis[x]=true;stack[++tmp]=x;
    bool flag=false;
    for (int i=point[x];i;i=nxt[i])
    {
        if (v[i]==fa&&!flag)
        {
            flag=true;
            continue;
        }
        if (!dfn[v[i]])
        {
            tarjan(v[i],x);
            low[x]=min(low[x],low[v[i]]);
        }
        else if (vis[v[i]]) low[x]=min(low[x],low[v[i]]);
    }
    if (dfn[x]==low[x])
    {
        dcc++;int now=0;
        while (now!=x)
        {
            now=stack[tmp--];
            belong[now]=dcc;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;++i)
    {
        scanf("%d%d",&e[i].x,&e[i].y);
        add(e[i].x,e[i].y);
    }
    for (int i=1;i<=n;++i)
        if (!dfn[i]) tarjan(i,0);
    for (int i=1;i<=m;++i)
        if (belong[e[i].x]!=belong[e[i].y])
            du[belong[e[i].x]]++,du[belong[e[i].y]]++;
    for (int i=1;i<=dcc;++i)
        if (du[i]==1) ans++;
    ans=(ans+1)>>1;
    printf("%d\n",ans);
}