题目链接:http://poj.org/problem?id=3177
题目大意:有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。

就是给你一个无向图,问你至少加多少条边能变成双连通图

分析:对图进行缩点,形成一棵树,树上度为1的点。必须向其他节点连边,才能使得这两个点和他们LCA上所有的点连通。所以肯定是度为1的节点向度为1的节点连边。这样一次消去两条度为1的点。

强连通: max(r(1), c(1));	r(1)入度为1的节点数  c(1)出度为1的节点数
双连通:(n+1)/2;      		n:度为1的节点数

具体做法:首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

错误做法:认为每一个双连通分量内的点low[]值都是相同的。多个环的时候是错误的。

样例:
5 6
1 2
2 3
3 4
4 2
4 5
5 1






代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
struct Node
{
    int to;
    int next; //起点相同的上一条边下标
    bool cut; //边是否为桥
}edge[maxn*2];

int cut;      //边数

int head[maxn];//点i的最后一条边

int dfn[maxn];
int low[maxn];
int tot;
int bvis[maxn];//点属于哪个边连通分量
int N;         //连通分量数

vector<int> BCC[maxn];

void addEdge(int u, int v)
{
    edge[cut].to=v;
    edge[cut].next=head[u];
    edge[cut].cut=0;
    head[u]=cut++;
}

//e是为了去重,e是边在数组的位置
//另一种去重为DFS(u,fa),v!=fa,但是有重边时可能会判断错误
//比如没重边时,假设(a,b)是桥,但是如果(a,b)有重边,那么(a,b)就不是桥了
void Tarjan(int u, int e)//求出所有的桥
{
    cout<<"P:"<<u<<endl;
    low[u]=dfn[u]=++tot;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(i==(e^1))//这里只会去重该边的反边,不会去它的重边
        {
            continue;
        }
        if(dfn[v]==0)
        {
            Tarjan(v, i);
            low[u]=min(low[u], low[v]);
            cout<<u<<' '<<low[u]<<' '<<v<<' '<<low[v]<<endl;

            if(low[v]>dfn[u])//桥
            {
                cout<<"hhh"<<endl;
                edge[i].cut=1;
                edge[i^1].cut=1;
            }
        }
        else if(dfn[v]<dfn[u])
        {
            low[u]=min(low[u], dfn[v]);
        }
    }
}

void dfs(int u)//求出每个点所在的边连通分量
{
    dfn[u]=1;
    bvis[u]=N;
    BCC[N].push_back(u);
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        if(edge[i].cut)
        {
            continue;
        }
        if(dfn[edge[i].to]==0)
        {
            dfs(edge[i].to);
        }
    }
}

int main()
{
    int n,m,a,b;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(head, -1, sizeof(head));

        cut=0;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&a,&b);
            addEdge(a, b);
            addEdge(b, a);
        }

        memset(dfn, 0, sizeof(dfn));
        memset(bvis, 0, sizeof(bvis));
        tot=N=0;
        for(int i=1;i<=n;i++)
        {
            if(dfn[i]==0)
            {
                Tarjan(i, -1);
            }
        }

        for(int i=1;i<=n;i++)
        {
            //cout<<dfn[i]<<' '<<low[i]<<endl;
        }

        memset(BCC, 0, sizeof(BCC));
        memset(dfn, 0, sizeof(dfn));

        for(int i=1;i<=n;i++)
        {
            if(dfn[i]==0)
            {
                N++;
                dfs(i);
            }
        }
        cout<<N<<endl;
    }

    return 0;
}