poj 3694 Network 双连通分量

双连通缩点后的图上任意边都是桥,且它是一棵树。

由此可知,若新加的一条边处于同一个双联通分量,那么答案不变。

否则,新加的边使得树上多出了一个环,且环上的桥都没有了,所以这两点路径上的边都要减少,预处理每个点的父亲和深度,那么求两点的路径直接暴力向上爬就行了,注意标记边只能减少一次。


#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define maxn 100005
#define maxm 2000005
struct node
{
    int to,vis,next;
}e[maxm],e2[maxm];
int belong[maxn],vis[maxn],dfn[maxn],low[maxn],cnt,bridge,col,stack[maxn],top,fa[maxn],head[maxn],head2[maxn],en,en2,ans;
bool use[maxm];
int n,m;
int dep[maxn];
void add(int a,int b)
{
    e[en].to=b;
    e[en].vis=0;
    e[en].next=head[a];
    head[a]=en++;
}
void add2(int a,int b)
{
    e2[en2].to=b;
    e2[en2].next=head2[a];
    head2[a]=en2++;
}
void init()
{
    top=cnt=col=bridge=en2=en=0;
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(use,0,sizeof(use));
    memset(head2,-1,sizeof(head2));
    memset(head,-1,sizeof(head));
}
void Tarjan (int u)
{
    int v;
    vis[u] = 1;
    dfn[u] = low[u] = ++cnt;
    stack[top++] = u;
    for (int i = head[u];i != -1;i = e[i].next)
    {
        v = e[i].to;
        if (e[i].vis) continue;
        e[i].vis = e[i^1].vis = 1;
        if (vis[v] == 1)
            low[u] = min(low[u],dfn[v]);
        if (!vis[v])
        {
            Tarjan (v);
            low[u] = min(low[u],low[v]);
            if (low[v] > dfn[u])
                bridge ++;
        }
    }
    if (dfn[u] == low[u])
    {
        ++col;
        do{
            v = stack[--top];
            vis[v] = 0;
            belong[v] = col;
        }while (u != v);
    }
}
void dfs(int now)
{
    for(int i=head2[now];~i;i=e2[i].next)
    {
        int to=e2[i].to;
        if(to!=fa[now])
        {
            dep[to]=dep[now]+1;
            fa[to]=now;
            dfs(to);
        }
    }
}
void cal(int a,int b)
{
    if(a==b) return;
    int root=belong[a];//小优化,可不加
    while(a!=b)
    {
        if(dep[a]<dep[b]) swap(a,b);
        if(use[a]==0) ans--;
        use[a]=1;
        a=fa[a];
        belong[a]=root;
    }
}
inline int ReadInt()
{
    char ch = getchar();
    int data = 0;
    while (ch < '0' || ch > '9')
    {
        ch = getchar();
    }
    do
    {
        data = data*10 + ch-'0';
        ch = getchar();
    }while (ch >= '0' && ch <= '9');
        return data;
}
int main()
{
    int a,b,q,ca=1;
    while(~scanf("%d%d",&n,&m))
    {
        if(!n&&!m) break;
        init();
        for(int i=1;i<=m;i++)
        {
            a=ReadInt();
            b=ReadInt();
            add(a,b);
            add(b,a);
        }
        Tarjan(1);
        ans=col-1;
        for(int i=1;i<=n;i++)
        {
            for(int j=head[i];~j;j=e[j].next)
            {
                int to=e[j].to;
                if(belong[to]!=belong[i])
                {
                    add2(belong[i],belong[to]);
                }
            }
        }
        dep[1]=0;
        fa[1]=-1;
        dfs(1);
        q=ReadInt();
        printf("Case %d:\n",ca++);
        while(q--)
        {
            a=ReadInt();
            b=ReadInt();
            cal(belong[a],belong[b]);
            printf("%d\n",ans);
        }
        puts("");
    }
    return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TommyTT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值