poj 3694

Network
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 6765 Accepted: 2403

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://www.cppblog.com/menjitianya/archive/2014/06/28/207449.html

题意:给定N(N <= 105)个点和M(N-1 <= M <= 2*105)条边的无向连通图,进行Q(Q <= 1000)次加边,每次加入一条边要求输出当前图中有多少条割边。

       题解:无向图割边、最近公共祖先

利用tarjan求出原图的割边,由于这题数据量比较大,所以用递归可能会爆栈,需要栈模拟实现递归过程,tarjan计算的时候用parent[u]保存u的父结点,每个结点进出栈各一次,出栈时表示以它为根结点的子树访问完毕,然后判断(u, parent[u])是否为割边。每次询问u, v加入后会有多少割边,其实就是求uv的到它们的最近公共祖先lca(u, v)的路径上有多少割边,由于在进行tarjan计算的时候保存了每个结点的最早访问时间dfn[u],那么有这么一个性质:dfn[ parent[u] ] < dfn[u],这是显然的(父结点的访问先于子结点)。于是当dfn[u] < dfn[v],将parent[v]赋值给v,反之,将parent[u]赋值给u,因为是一棵树,所以进过反复迭代,一定可以出现u == v 的情况,这时候的u就是原先uv的最近公共祖先,在迭代的时候判断路径上是否存在割边,路径上的割边经过(u, v)这条边的加入都将成为非割边,用一个变量保存割边数目,输出即可。


3

       如图3,图中实线表示树边,虚线表示原图中的边,但是进行tarjan计算的时候7这个结点被(6, 7)这条边“捷足先登”了,于是(4, 7)成为了一条冗余边,计算完后这个图的割边为(1, 2)(1,3)(3, 4)(3, 5),分别标记bridge[2]bridge[3]bridge[4]bridge[5]true

当插入一条边(7, 5),那么沿着7的祖先路径和5的祖先路径最后找到的最近公共祖先为3(路径为7 -> 6 -> 4 -> 3  5 -> 3)(3, 4)(3, 5)这两条割边因为加入了(7, 5)这条边而变成了普通边,将标记bridge[4]bridge[5]置为false


AC代码:

#include<algorithm>
#include<iostream>
#include<stdio.h>
using namespace std;
struct Node{
    int v,next;
}Edge[400010];
int ed,head[200010];
int pre[100010],low[100010],isb[100010],f[100010];
int step;
int n,m,bridge;
void init(){
    bridge=ed=step=0;
    for(int i=1;i<=n;i++){
       isb[i]=head[i]=low[i]=pre[i]=0;
    }
}
void add(int x,int y){
    ed++;
    Edge[ed].v=y; Edge[ed].next=head[x]; head[x]=ed;
}
void dfs(int u,int fa){          //求桥的数量,并确定每个点的父节点
    pre[u]=low[u]=++step;
    for(int i=head[u];i;i=Edge[i].next){
        int v=Edge[i].v;
        if(!pre[v]){
            f[v]=u;
            dfs(v,u);
            low[u]=min(low[v],low[u]);
            if(low[v]>pre[u]){
                bridge++;
                isb[v]=1;
            }
        }
        else if(pre[v]<pre[u] && v!=fa){
            low[u]=min(pre[v],low[u]);
        }
    }
}
void lca(int u,int v){                 //最近公共祖先
    while(pre[u]>pre[v]){
        if(isb[u]){
            isb[u]=0;
            bridge--;
        }
        u=f[u];
    }
    while(pre[v]>pre[u]){
        if(isb[v]){
            isb[v]=0;
            bridge--;
        }
        v=f[v];
    }
    while(u!=v){
        if(isb[u]){
            isb[u]=0;
            bridge--;
        }
        if(isb[v]){
            isb[v]=0;
            bridge--;
        }
        u=f[u];
        v=f[v];
    }
}
int main(){
    int cas=1;
    while(scanf("%d%d",&n,&m)!=EOF){
        if(n==0 && m==0)
            break;
        init();
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        for(int i=1;i<=n;i++){
            if(!pre[i]){
                dfs(i,-1);
            }
        }
        scanf("%d",&m);
        printf("Case %d:\n",cas++);
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            lca(x,y);
            printf("%d\n",bridge);
        }
        printf("\n");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值