题目描述: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;
}