poj 3694 Network(桥+边联通分量+LCA)

题目链接

Network
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 6942 Accepted: 2476

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≤ AB ≤ 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 ≤ ABN), 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

题意:给一个联通的无向图,Q次询问,每次询问给图动态加一条边,输出加完这条边后图的桥的个数。

题解:将边联通分量缩点。这题数据不强,每次加完边后求tarjan求桥的个数也能过。

缩完点后图一定是树。以这棵树的任意一个节点为根节点,遍历整棵树,记录每个节点的父亲节点,并进行预处理,在询问的时候求LCA。

对于每次询问:(a,b)

求出a,b的最近公共祖先c。依次统计当前的图a到c的边中有多少条桥边,b到c的边中有多少条桥边。统计完以后,由于a到c的边,b到c的边已经不是桥边,我们可以用 f[i] 记录第i个点到它深度最小的祖先,且满足它到该祖先没有桥边。那么询问的总复杂度为O(N)。

代码如下:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stack>
#include<vector>
#include<math.h>
#include<stdlib.h>
#define nn 110000
#define inff 0x3fffffff
using namespace std;
int n,m;
struct node
{
    int st,en,next;
}E[nn*10];
int p[nn],num;
void init()
{
    memset(p,-1,sizeof(p));
    num=0;
}
void add(int st,int en)
{
    E[num].st=st;
    E[num].en=en;
    E[num].next=p[st];
    p[st]=num++;
}
int dfn[nn],low[nn];
int df;
bool qiao[4*nn];
int clo[nn];
int cnt;
int fa[nn];
bool use[nn];
vector<int>ve;
vector<int>tu[nn];
void dfs(int id,int pre)
{
    dfn[id]=low[id]=++df;
    int i,w;
    for(i=p[id];i+1;i=E[i].next)
    {
        w=E[i].en;
        if((i^1)==pre)
            continue;
        if(dfn[w]==-1)
        {
            dfs(w,i);
            if(low[w]>dfn[id])
            {
                ve.push_back(i);
                qiao[i]=qiao[i^1]=true;
            }
            low[id]=min(low[id],low[w]);
        }
        else
            low[id]=min(low[id],dfn[w]);
    }
}
void go(int id)
{
    clo[id]=cnt;
    int i,w;
    for(i=p[id];i+1;i=E[i].next)
    {
        if(qiao[i])
            continue;
        w=E[i].en;
        if(clo[w]==-1)
            go(w);
    }
}
int dep[nn*2];
int vis;
int pos[nn];
int fang[nn*2];
int dp[nn*2][20];
void DFS(int id,int pre,int shen)
{
    fa[id]=pre;
    dep[++vis]=shen;
    fang[vis]=id;
    pos[id]=vis;
    int i,w;
    for(i=p[id];i+1;i=E[i].next)
    {
        w=E[i].en;
        if(w==pre)
            continue;
        DFS(w,id,shen+1);
        dep[++vis]=shen;
        fang[vis]=id;
    }
}
void rmq_init()
{
    int i,j;
    for(i=1;i<=vis;i++)
    {
        dp[i][0]=i;
    }
    for(j=1;(1<<j)<=vis;j++)
    {
        for(i=1;i+(1<<j)-1<=vis;i++)
        {
            if(dep[dp[i][j-1]]<dep[dp[i+(1<<(j-1))][j-1]])
                dp[i][j]=dp[i][j-1];
            else
                dp[i][j]=dp[i+(1<<(j-1))][j-1];
        }
    }
}
void solve()
{
    int i;
    memset(dfn,-1,sizeof(dfn));
    df=0;
    memset(qiao,false,sizeof(qiao));
    ve.clear();
    dfs(1,-1);
    memset(clo,-1,sizeof(clo));
    cnt=0;
    for(i=1;i<=n;i++)
    {
        if(clo[i]==-1)
        {
            cnt++;
            go(i);
        }
    }
    int ix,u,v;
    init();
    sort(ve.begin(),ve.end());
    for(i=0;i<(int)ve.size();i++)
    {
        ix=ve[i];
        u=clo[E[ix].st],v=clo[E[ix].en];
        add(u,v);
        add(v,u);
    }
    vis=0;
    DFS(1,1,0);
    rmq_init();
}
int rmq(int l,int r)
{
    if(l>r)
        swap(l,r);
    int k=0;
    while((1<<(k+1))<r-l+1)
    {
        k++;
    }
    if(dep[dp[l][k]]<dep[dp[r-(1<<k)+1][k]])
        return dp[l][k];
    return dp[r-(1<<k)+1][k];
}
int main()
{
    int i,q;
    int u,v;
    int cas=1;
    while(scanf("%d%d",&n,&m)&&n+m)
    {
        init();
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        solve();
        scanf("%d",&q);
        int ix,fc;
        int nq=ve.size();
        printf("Case %d:\n",cas++);
        memset(use,false,sizeof(use));
        while(q--)
        {
            scanf("%d%d",&u,&v);
            u=clo[u],v=clo[v];
            ix=rmq(pos[u],pos[v]);
            ix=fang[ix];
            while(dep[pos[u]]>dep[pos[ix]])
            {
                if(!use[u])
                {
                    use[u]=true;
                    nq--;
                }
                fc=u;
                u=fa[u];
                if(dep[pos[u]]>dep[pos[ix]])
                    fa[fc]=ix;
            }
            while(dep[pos[v]]>dep[pos[ix]])
            {
                if(!use[v])
                {
                    use[v]=true;
                    nq--;
                }
                fc=v;
                v=fa[v];
                if(dep[pos[v]]>dep[pos[ix]])
                    fa[fc]=ix;
            }
            printf("%d\n",nq);
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值