题目描述:n个地点,m条路,要规划旅游路线(经过多个地点),必须是个环形。如果一条路属于多条旅游路线,就可能发生相撞,如果一条路不属于任何旅游路线,就统计一下。要求输出不属于任何旅游路线的路的数目以及会相撞的路的数目。


题解:点双联通分量问题。第一问:桥的数量;第二问:如果一个联通分量中边的数目大于点的数目,则所有边都会发生危险。


代码:

#include<bits/stdc++.h>
#define N 10010
#define mem(f,n) memset(f,n,sizeof f)
using namespace std;

struct edge
{
    int u,v,w;
}G[N*20];

int pre[N],bn[N],dfs_clock,bcc_cnt,fr[N],t,ans1,ans2,low[N];
stack<edge>s;

void addedge(int u,int v)
{
    G[++t].u=u;G[t].v=v;
    G[t].w=fr[u];
    fr[u]=t;
}

void dfs(int u,int fa)
{
    low[u]=pre[u]=++dfs_clock;
    for (int i=fr[u];i!=-1;i=G[i].w)
    {
        int v=G[i].v;
        if (v==fa)continue;
        if (!pre[v])
        {
            s.push(G[i]);
            dfs(v,u);
            low[u]=min(low[u],low[v]);
            if (low[v]>pre[u])  ans1++;
            if (low[v]>=pre[u])
            {
                bcc_cnt++;
                int num=0,num1=0;
                for (;;)
                {
                    num++;                        //边的数量
                    edge x=s.top();s.pop();
                    if (bn[x.u]!=bcc_cnt){ num1++;bn[x.u]=bcc_cnt;}  //点的数量
                    if (bn[x.v]!=bcc_cnt){ num1++;bn[x.v]=bcc_cnt;}
                    if (x.u==u && x.v==v) break;
                }
                if (num>num1) ans2+=num;
            }
        }else
        if (pre[v]<pre[u])
        {
            s.push(G[i]);
            low[u]=min(low[u],pre[v]);
        }
    }
}

int n,m;
int main()
{
    while (~scanf("%d%d",&n,&m)&& n>0)
    {
        t=ans1=ans2=dfs_clock=bcc_cnt=0;
        mem(bn,0);mem(fr,-1);mem(pre,0);
        while (!s.empty())s.pop();
        while (m--)
        {
            int j,k;
            scanf("%d%d",&j,&k);
            addedge(j,k);
            addedge(k,j);
        }
        for (int i=0;i<n;i++) if (!pre[i]) dfs(i,-1);
        printf("%d %d\n",ans1,ans2);
    }
    return 0;
}