POJ - 3694 Network(tarjan+lca)

题意:给出一个无相图,然后q次新增加边,问在添加边的过程中桥的数目
当且仅当无向边(u,v)为树枝的时候,需要满足dfn(u)<low(v),
也就是v向上翻不到u及其以上的点,那么u-v之间一定能够有1条或者多条边不能删去,因为他们之间有一部分无环,是桥
思路:首先我们知道在给定一张图之后,不断添加边,桥的数目只会减少而不是增加
tarjan的使用就是缩点,将一个连通分量缩成一个点,那么那个连通分量中的边都不会是桥
同时缩完点之后我们就会发现,桥其实就是新形成的树的边
在添加的过程中,如果是在连通分量内就不会减少桥的数目
如果是u,v在两个不同的连通分量中,那么u,v以及其lca(最小公共祖先)就会连成一个回路,就形成了一个新的连通分量
那么被连接的这整个一段部分的所有桥的数目就会减少

完整代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<int,int>pii;
const double eps = 1e-8;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5+10;
const int max_edge = 4e5+5;
struct Edge
{
    int to, next;
}edge[max_edge];
int top, head[maxn];
int pre[maxn];
int id, dfn[maxn], low[maxn];
int cnt;
int bridge[maxn];
int n,m;
void add(int u,int v){
    edge[top].to = v;
    edge[top].next = head[u];
    head[u] = top++;
}
void tarjan(int x,int f){
    //更新时间戳
    dfn[x] = low[x] = ++id;
    for(int i = head[x]; ~i;i = edge[i].next){
        int v = edge[i].to;
        if(!dfn[v]){
            tarjan(v,x);
            pre[v] = x;
            low[x] = min(low[v],low[x]);
            //桥的判定条件
            if(low[v]>dfn[x]){
                bridge[v] = 1;
                cnt++;
            }
        }
        else if(v != f){
            low[x] = min(low[x],dfn[v]);
        }

    }
}
void init(){
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    memset(bridge,0,sizeof(bridge));
    memset(head,-1,sizeof(head));
   cnt = id = top = 0;
}
void lca(int u,int v){
    //进入时交换一下,把根赋值给u
    if(dfn[u] < dfn[v]) swap(u,v);
    //先将底的一边走到同样的高度
    while(dfn[u] > dfn[v]){
        if(bridge[u]) cnt--,bridge[u]=0;
        u = pre[u];
    }
    //两边同时向lca走
    while(u != v){
        if(bridge[u]) cnt--,bridge[u] = 0;
        if(bridge[v]) cnt--,bridge[v] = 0;
        u = pre[u];
        v = pre[v];
    }
}
int main(){
    int Case = 0;
    while(cin>>n>>m&&n&&m){
        init();
        for(int i =0;i<m;i++){
            int u,v;
            cin>>u>>v;
            add(u,v),add(v,u);
        }
        tarjan(1,1);
        int q;
        cin>>q;
        cout<<"Case "<<++Case<<":"<<endl;
        while(q--){
            int u,v;
            cin>>u>>v;
            lca(u,v);
            cout<<cnt<<endl;
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Tianwell/p/11328947.html