HDU3394Railway Tarjan连通算法
Now we know the plan, and can you tell us how many railways are no need to build and how many railways where clash might happen.
InputThe Input consists of multiple test cases. The first line of each test case contains two integers, n (0 < n <= 10000), m (0 <= m <= 100000), which are the number of locations and the number of the railways. The next m lines, each line contains two integers, u, v (0 <= u, v < n), which means the manger plans to build a railway on the road between u and v.
You can assume that there is no loop and no multiple edges.
The last test case is followed by two zeros on a single line, which means the end of the input.OutputOutput the number of railways that are no need to build, and the number of railways where clash might happen. Please follow the format as the sample.Sample Input
8 10
0 1
1 2
2 3
3 0
3 4
4 5
5 6
6 7
7 4
5 7
0 0
Sample Output
1 5
过了一周再来总结这几道题,为什么这道题是每条边访问后就退栈stk[top--],而“warm up”是访问完所以边再退栈。是因为此题不缩点,只是取值,所以不必等访问完所有边再退栈。相反,1-2-4组成一个环,1-3-5组成一个环,点相联通的情况下1-2-4-3-5都应该再这个缩点里,访问完一条边就退栈会导致1-2-4和3-5不在一个缩点里。
比如HDU2242不需要缩点,可以遇到一割边就处理。而HDU4612就必须访问完相连的割边再缩点。
又过了一周,再总结:
点的双连通存桥(边),每访问一条边操作一次。
边的双连通存割点(点),访问完所有边后操作。
尚有疑惑
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=10010;
const int maxm=200010;
int Laxt[maxn],Next[maxm],To[maxm],cnt,vis[maxn];
int dfn[maxn],low[maxn];
int times,ans,cute_cnt,n,m;
int q[maxn],q_cnt,scc[maxn],scc_cnt;
int stk[maxn],top;
void _init()
{
memset(Laxt,0,sizeof(Laxt));
memset(dfn,0,sizeof(dfn));
memset(scc,0,sizeof(scc));
memset(vis,0,sizeof(vis));
ans=cute_cnt=top=scc_cnt=cnt=times=0;
}
void _add(int u,int v)
{
Next[++cnt]=Laxt[u];
Laxt[u]=cnt;
To[cnt]=v;
}
void _count()//找环
{
int e=0;
for(int i=1;i<=q_cnt;i++)
for(int j=Laxt[q[i]];j;j=Next[j])
if(scc[To[j]]==scc[q[i]]) e++;
e/=2;
if(e>q_cnt) ans+=e;
}
void _tarjan(int u,int v){
dfn[u]=low[u]=++times;
int num_v=0;
stk[++top]=u;
for(int i=Laxt[u];i;i=Next[i]){
if(To[i]==v) continue;//此题无重边
if(!dfn[To[i]]){
_tarjan(To[i],u);
if(low[u]>low[To[i]]) low[u]=low[To[i]];
if(low[To[i]]>dfn[u]) cute_cnt++;//割边
if(dfn[u]<=low[To[i]]){//小于是个环,等于是个点,都要处理
q_cnt=0;//环内的点
scc_cnt++;
for(;;){
int tmp=stk[top--];
scc[tmp]=scc_cnt;
q[++q_cnt]=tmp;
if(tmp==To[i]) break;
}
scc[u]=scc_cnt;
q[++q_cnt]=u;
_count();
}
}
else if(dfn[To[i]]<low[u]) low[u]=dfn[To[i]];
}
}
int main()
{
int i,j,k,u,v;
while(~scanf("%d%d",&n,&m)){
if(n==0&&m==0) return 0;
_init();
while(m--){
scanf("%d%d",&u,&v);
u++;v++;
_add(u,v);
_add(v,u);
}
for(i=1;i<=n;i++)
if(!dfn[i]) _tarjan(i,-1);
printf("%d %d\n",cute_cnt,ans);
}
return 0;
}