POJ 3694 Network tarjan求桥+回溯求祖先

46 篇文章 0 订阅
19 篇文章 0 订阅

Network
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 6241 Accepted: 2178

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

题意大体就是:给你一个联通图,然后再向上面加n条边,然后判断加完每次边之后这个图上还有多少个桥存在。

我一开始感觉是加边然后判断还有多少条桥存在。效率会很低啊,又听说一种找最近公共祖先的办法,就学习了一下啊确实不错噢,鹏哥给解释很详细、、、

算法的思想就是:tarjan求出所有的桥,缩点成一个树,然后在两个节点上回溯一直找到他们的公共祖先为止,然后记录下来这两条链上一共有多少个桥存在,这时候所有的桥就会被连接起来不再是桥,所以减去他们的个数,就是加完边之后还剩下的桥个数。

#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <stdio.h>
#include <string>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
#define eps 1e-7
#define M 1000100
#define LL __int64
#define INF 0x3f3f3f3f
#define PI 3.1415926535898

const int maxn = 1000100;
using namespace std;

struct node
{
    int u, v;
    int next;
} f[maxn];
int head[maxn];
int low[maxn], dfn[maxn];
int vis[maxn];
int fa[maxn];
int isb[maxn];
int n, m, cnt;
int t, dfs_clock;

void init()
{
    t = 0;
    memset(head, -1 , sizeof(head));
    memset(vis, 0 , sizeof(vis));
    for(int i = 0; i <= n; i++) fa[i] = i;
    cnt = 0;
    memset(low, 0 , sizeof(low));
    memset(dfn, 0 , sizeof(dfn));
    memset(isb, 0 , sizeof(isb));
    dfs_clock = 0;
}
void add(int x, int y)
{
    f[t].u = x;
    f[t].v = y;
    f[t].next = head[x];
    head[x] = t++;
}

void tarjan(int u)
{
    dfn[u] = low[u] = ++dfs_clock;
    for(int i = head[u]; i != -1; i = f[i].next)
    {
        if(vis[i]) continue;
        vis[i] = vis[i^1] = 1;//标记路径是否存在
        int v = f[i].v;
        if(!dfn[v])//如果v没有被访问过
        {
            fa[v] = u;
            tarjan(v);
            low[u] = min(low[u], low[v]);
            if(low[v] > dfn[u])
            {
                cnt++;
                isb[v] = 1;
            }
        }
        else
            low[u] = min(low[u], dfn[v]);//更新
    }
}

void LCA(int x, int y)//就是不停地回溯,判断是否此时的祖先是否相同
{
    while(x != y)
    {
        if(isb[x])
        {
            cnt--;
            isb[x] = 0;
        }
        if(isb[y])
        {
            cnt--;
            isb[y] = 0;
        }
        x = fa[x];
        y = fa[y];
    }
}

int main()
{
    int _case = 1;
    int i;
    while(cin >>n>>m)
    {
        if(!n && !m)
            break;
        init();
        int u,v;
        for(i = 0; i < m; i++)
        {
            cin >>u>>v;
            add(u, v);
            add(v, u);
        }
        tarjan(1);//这里的图已经确定是连通的了,所以就从一个点出发找就可以了啊
        int k;
        cout<<"Case "<<_case++<<":"<<endl;
        cin >>k;
        while(k--)
        {
            cin >>u>>v;
            LCA(u, v);
            cout<<cnt<<endl;
        }
        cout<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值