天天看点

poj 3694 (求桥+LCA问题)

Network

Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 3868 Accepted: 1321

Description

A network administrator manages a large network. The network consists of N computers and M links between pairs of computers. Any pair of computers are connected directly or indirectly by successive links, so data can be transformed between any two computers. The administrator finds that some links are vital to the network, because failure of any one of them can cause that data can't be transformed between some computers. He call such a link a bridge. He is planning to add some new links one by one to eliminate all bridges.

You are to help the administrator by reporting the number of bridges in the network after each new link is added.

Input

The input consists of multiple test cases. Each test case starts with a line containing two integers N(1 ≤ N ≤ 100,000) and M(N - 1 ≤ M ≤ 200,000).

Each of the following M lines contains two integers A and B ( 1≤ A ≠ B ≤ N), which indicates a link between computer A and B. Computers are numbered from 1 to N. It is guaranteed that any two computers are connected in the initial network.

The next line contains a single integer Q ( 1 ≤ Q ≤ 1,000), which is the number of new links the administrator plans to add to the network one by one.

The i-th line of the following Q lines contains two integer A and B (1 ≤ A ≠ B ≤ N), which is the i-th added new link connecting computer A and B.

The last test case is followed by a line containing two zeros.

Output

For each test case, print a line containing the test case number( beginning with 1) and Q lines, the i-th of which contains a integer indicating the number of bridges in the network after the first i new links are added. Print a blank line after the output for each test case.

Sample Input

3 2
1 2
2 3
2
1 2
1 3
4 4
1 2
2 1
2 3
1 4
2
1 2
3 4
0 0      

Sample Output

Case 1:
1
0

Case 2:
2
0      

Source

2008 Asia Hefei Regional Contest Online by USTC 题目: http://poj.org/problem?id=3694 分析:这题一开始就想到求桥,然后用LCA(最小公共祖先)来解决,不过由于本人比较挫,LCA不熟,一直想搞一个简单一点的做法,还是没搞出来,做法一般有两种: 第一种:比较好理解,切效率比较高的一般都是求出所有边连通块,将它缩成一个点,这样原图就变成一棵树,每次询问就找两个点所对应的新点,然后看他们与他们的LCA之间有几条边s,把总变数-s,然后把三个点直接所有点再缩成一点,这样实现起来实在是。。。。 第二种:在求桥的同时已经给图分层了,我们没必要把新图构出来,只需在原图根据定理来做就行,记录属于割边的点(后遍历到的),还有所有点的反向边(即通过那条边遍历过来),这样,每次询问两个点时,先把他们移到同一层,然后判断两点是否共点,如果不是两点同时往上移,直到共点,移动过程中遇到标记为桥的点,把答案减一,改变标记就行。。。 第二种解法能过实在是侥幸,如果这题出现一组数据使得该图是一条直线,然后每次询问头和尾,这样复杂度升级为(QV),实在是很可怕阿~~~~ 明天补上第一种写法把,囧 好吧,其实昨天想了一种类似并查集的方法来优化第二种,不过写挫了,今天仔细看了下,原来是低级错误。。。 只要记录查LCA时路径上的点,把他们的父节点全部变成LCA,时间290++ms,感觉还好 代码( 红色部分为改动部分):

#include<cstdio>
#define min(a,b) (a<b?a:b)
using namespace std;
const int mm=444444;
const int mn=111111;
int t[mm],p[mm];
int h[mn],pre[mn],dfn[mn],low[mn];
bool bg[mn];
int i,j,k,n,m,q,top,idn,sum,cas=0;
void dfs(int u)
{
    dfn[u]=low[u]=++idn;
    for(int i=h[u],v;i>=0;i=p[i])
        if(!dfn[v=t[i]])
        {
            pre[v]=u,dfs(v);
            low[u]=min(low[u],low[v]);
             if(dfn[u]<low[v])++sum,bg[v]=1;
        }
        else if(v!=pre[u])low[u]=min(low[u],dfn[v]);
}
void tarjan()
{
    for(int i=0;i<=n;++i)dfn[i]=bg[i]=0;
    idn=sum=0,dfs(1);
}
void move(int &x)
{
    if(bg[x])--sum,bg[x]=0;
    p[top++]=(x=pre[x]);//记录节点
}
int lca(int x,int y)
{
    p[0]=x,p[1]=y,top=2;//初始化栈
    while(x!=y)
    {
        if(dfn[x]>dfn[y])move(x);
        else if(dfn[x]<dfn[y])move(y);
        else move(x),move(y);
    }
    while(top--)if(p[top]!=x)pre[p[top]]=x;//更改父节点
    return sum;
}
int main()
{
    while(scanf("%d%d",&n,&m),n+m)
    {
        for(i=k=0;i<=n;++i)h[i]=-1;
        while(m--)
        {
            scanf("%d%d",&i,&j);
            t[k]=j,p[k]=h[i],h[i]=k++;
            t[k]=i,p[k]=h[j],h[j]=k++;
        }
        tarjan();
        scanf("%d",&q);
        printf("Case %d:\n",++cas);
        while(q--)
        {
            scanf("%d%d",&i,&j);
            printf("%d\n",lca(i,j));
        }
        puts("");
    }
    return 0;
}
           

继续阅读