POJ3694 Network

题目描述

输入格式

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).

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

输出格式


大致翻译:给你N个点M条边的无向图,并且有Q次加边,问每次加边之后图中的桥的数量。

显然,如果加入的边的两个端点在同一个边双内,那么桥的数量不变。所以我们先用Tarjan对原图进行边双连通分量缩点得到一棵树。

接着,对于两个端点不在一个边双的情况,显然桥的数量减少量等于两个端点的树上距离。我们求出树上距离,然后把两端点之间的边标记起来,即边长由原来的1改成0。每次求树上距离时就先一个个地往上爬,顺便还可以标记边。时间复杂度为O(M+QN),可以通过本题,但显然不优。

既然边长变成0了,我们以后都没必要再管这些边了,所以我们可以用缩树的办法,用并查集把两个端点之间的点合并到一个集合中去,然后下次爬到这两个端点处时直接跳到LCA的位置就好了。

时间复杂度为O(M+Qα(N)),接近线性

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define maxn 100010
#define maxm 200010
using namespace std;

struct edge{
    int to,next;
    edge(){}
    edge(const int &_to,const int &_next){ to=_to,next=_next; }
}e[maxm<<1];
int head[maxn],k;

int dep[maxn],pre[maxn],fa[maxn];
int dfn[maxn],low[maxn],tot,stack[maxn],top;
int uu[maxm],vv[maxm];
int n,m,t,ans;

inline void init(){
    for(register int i=0;i<=n;i++) head[i]=-1,dep[i]=dfn[i]=0;
    k=top=ans=tot=0;
}
inline void add(const int &u,const int &v){ e[k]=edge(v,head[u]),head[u]=k++; }

void tarjan_dcc(int u,int pre){
    if(dfn[u]) return;
    dfn[u]=low[u]=++tot,stack[++top]=u;
    bool flag=true;
    for(register int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(v==pre&&flag) { flag=false; continue; }
        tarjan_dcc(v,u),low[u]=min(low[u],low[v]);
    }
    if(dfn[u]==low[u]){
        while(stack[top]!=u) low[stack[top--]]=low[u];
        top--;
    }
}
inline void rebuild(){
    for(register int i=0;i<=n;i++) head[i]=-1; k=0;
    for(register int i=0;i<m;i++){
        int u=low[uu[i]],v=low[vv[i]];
        if(u!=v) add(u,v),add(v,u);
    }
}

void dfs(int u,int father,int deep){
    dep[u]=deep,pre[u]=u;
    for(register int i=head[u];~i;i=e[i].next){
        int v=e[i].to;
        if(v!=father) dfs(v,u,deep+1),fa[v]=u,ans++;
    }
}

int get(int x){
    if(x!=pre[x]) pre[x]=get(pre[x]);
    return pre[x];
}
inline void getlca(int u,int v){
    u=get(u),v=get(v),top=0;
    while(u!=v){
        if(dep[u]>dep[v]) stack[++top]=u,u=get(fa[u]);
        else stack[++top]=v,v=get(fa[v]);
        ans--;
    }
    while(top) pre[stack[top--]]=u;
}

int main(){
    int T=0;
    while(~scanf("%d %d",&n,&m),n+m){
        init();
        for(register int i=0;i<m;i++){
            scanf("%d %d",&uu[i],&vv[i]);
            add(uu[i],vv[i]),add(vv[i],uu[i]);
        }
        tarjan_dcc(1,-1),rebuild();
        dfs(low[1],-1,1);

        scanf("%d",&t);
        printf("Case %d:\n",++T);
        while(t--){
            int u,v; scanf("%d %d",&u,&v);
            getlca(low[u],low[v]);
            printf("%d\n",ans);
        }
        printf("\n");
    }
    return 0;
}
posted @ 2019-06-04 21:01  修电缆的建筑工  阅读(296)  评论(0编辑  收藏  举报