poj3694--Network(双连通缩点+lca)

poj3694:题目链接

题目大意:给出n个点,m条无向边的图,图中存在割边,问每加入一条新的边后的割边的数量

首先,进行双连通缩点,缩点后的图变成一棵树,树上的每条边都是割边,然后没加入一条新的边后,会使这条边的两个点到这两个点的lca形成一个环,使原本的割边减少。

图学的不好,只能显式建树,后来发现建树后没什么用,等以后再修改了

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std ;
#define maxn 110000
struct node
{
    int u , v ;
    int next ;
} edge[maxn<<2] , tree[maxn<<2] ;
int head[maxn] , cnt , vis[maxn<<2] ;
int h_tree[maxn] ;
int dnf[maxn] , low[maxn] , time ;
int belong[maxn] , degree[maxn] , num ;
int fa[maxn] ;
stack <int> sta ;
void init()
{
    while( !sta.empty() ) sta.pop() ;
    memset(head,-1,sizeof(head)) ;
    memset(vis,0,sizeof(vis)) ;
    memset(dnf,0,sizeof(dnf)) ;
    memset(low,0,sizeof(low)) ;
    memset(belong,0,sizeof(belong)) ;
    memset(degree,0,sizeof(degree)) ;
    cnt = time = num = 0 ;
}
void add(int u,int v)
{
    edge[cnt].u = u ;
    edge[cnt].v = v ;
    edge[cnt].next = head[u] ;
    head[u] = cnt++ ;
}
void add_tree(int u,int v) {
    tree[cnt].u = u ;
    tree[cnt].v = v ;
    tree[cnt].next = h_tree[u] ;
    h_tree[u] = cnt++ ;
}
void tarjan(int u)
{
    dnf[u] = low[u] = ++time ;
    int i , j , v ;
    for(i = head[u] ; i != -1; i = edge[i].next)
    {
        if( vis[i] ) continue ;
        vis[i] = vis[i^1] = 1 ;
        v = edge[i].v ;
        if( !dnf[v] )
        {
            sta.push(i) ;
            tarjan(v) ;
            low[u] = min(low[u],low[v]) ;
            if( low[v] > dnf[u] )
            {
                num++ ;
                while( 1 )
                {
                    j = sta.top() ;
                    sta.pop() ;
                    belong[ edge[j].v ] = num ;
                    if( edge[j].u == u ) break ;
                    belong[ edge[j].u ] = num ;
                }
            }
        }
        else if( dnf[v] < dnf[u] )
        {
            sta.push(i) ;
            low[u] = min(low[u],dnf[v]) ;
        }
    }

}
void dfs(int u) {
    int i , v ;
    for(i = h_tree[u] ; i != -1 ; i = tree[i].next) {
        v = tree[i].v ;
        if( vis[v] ) continue ;
        vis[v] = 1 ;
        fa[v] = u ;
        add(u,v) ;
        dfs(v) ;
    }
}
void get_tree(int m)
{
    int i , u , v ;
    memset(h_tree,-1,sizeof(h_tree)) ;
    cnt = 0 ;
    for( i = 0 ; i < m ; i++)
    {
        u = belong[ edge[i*2].u ] ;
        v = belong[ edge[i*2].v ] ;
        if( u != v )
        {
            add_tree(u,v) ;
            add_tree(v,u) ;
        }
    }
    memset(vis,0,sizeof(vis)) ;
    memset(head,-1,sizeof(head)) ;
    cnt = 0 ;
    fa[1] = 0 ;
    vis[1] = 1 ;
    dfs(1) ;
}
int solve(int u,int v) {
    int i , sum = 0 , flag = 0 ;
    memset(low,0,sizeof(low)) ;
    while( u != 0 ) {
        low[u] = 1 ;
        u = fa[u] ;
    }
    while( v != 0 ) {
        if( low[v] == 0 ) {
            low[v] = 1 ;
        }
        else {
            if( flag == 0 ) {
                flag = 1 ;
            }
            else {
                low[v] = 0 ;
            }
        }
        v = fa[v] ;
    }
    flag = 0 ;
    for(i = 1 ; i <= num ; i++) {
        if( low[i] && vis[i] )
            flag = 1 ;
        if( low[i] && !vis[i] ) {
            vis[i] = 1 ; sum++ ;
        }
    }
    return sum+flag ;
}
int main()
{
    int step = 0 , n , m , q , ans ;
    int u , v , i , k ;
    while( scanf("%d %d", &n, &m) && n+m > 0 )
    {
        init() ;
        for(i = 0 ; i < m ; i++)
        {
            scanf("%d %d", &u, &v) ;
            add(u,v) ;
            add(v,u) ;
        }
        tarjan(1) ;
        if( belong[1] == 0 ) {
            ++num ;
            for(i = 1 ; i<= n ; i++)
                if( belong[i] == 0 ) belong[i] = num ;
        }
        get_tree(m) ;
        ans = num ;
        memset(vis,0,sizeof(vis)) ;
        scanf("%d", &q) ;
        printf("Case %d:\n", ++step) ;
        while( q-- ) {
            scanf("%d %d", &u, &v) ;
            u = belong[u] ;
            v = belong[v] ;
            ans = ans - solve(u,v) + 1 ;
            printf("%d\n", ans-1) ;
        }
    }
    return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值