POJ 3694 Network

POJ 3694 Network

双连通分量 targan

传送门:HustOJ

传送门:POJ


题意

给你一个连通无向图,10w点。有Q次查询,每次查询a,b,先在ab间连一条路,再问全图剩下多少桥。


思路

这题可以用targan求出双连通分量,重新建图,用并查集维护连通块。不过很麻烦,每次加边时合并谁是个问题。

感觉这个人讲的很清楚。

实际上,这道题也不一定要缩点,如果用缩点的思路来做的话,程序将十分麻烦。可以直接根据dfn值来进行LCA。因为,我们观察low[v] > dfn[u]这个条件,代表的意思就是v无法通过回边或者通过子女到达比u点更靠前的点,那么我们只需要标记v点即可表明割边。在进行LCA时,由于树的组成就是原图中的割边,所以在原图中,根据这个标记来判断是否将割边被转化为了普通边。

我再解释一下,就是targan的dfs时,求出每个点的爸爸,然后如果low[to]>dfn[n],就表明to以及to的后代回不到n以前,那么to和n的边就是桥。标记一下to点就行了。

然后求LCA时,用最普通的方法,往爸爸上跳就行,跳的过程中发现了桥,那么这个桥就不是桥了,cnt–。因为targan后变成了一棵树,即使我们没有实际缩点,每个点都有了父亲,图实际已经变成一棵树了。将树的两个叶子连起来,路上所有点自然会成环,桥就不存在了。


代码

4000ms+卡过

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>

#define _ ios_base::sync_with_stdio(0),cin.tie(0)
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;

const int MAXN=100005;
const int oo=0x3f3f3f3f;
typedef long long LL;
const LL loo=4223372036854775807ll;
typedef long double LB;
const LL mod=1e9+7;

int father[MAXN];
int low[MAXN], dfn[MAXN], isbridge[MAXN], lev[MAXN];
vector<int> G[MAXN];
int dfs_clock=0;
int cnt=0;

void targan(int n)
{
    low[n]=dfn[n]=++dfs_clock;
    lev[n]=lev[father[n]]+1;
    for(int i=0;i<G[n].size();i++)
    {
        int to=G[n][i];
        if(!dfn[to])
        {
            father[to]=n;
            targan(to);
            low[n]=min(low[n], low[to]);
            if(low[to]>dfn[n])//to以及to的后代 回不去n之前的点
            {
                cnt++;
                isbridge[to]=1;
            }
        }
        else if(to!=father[n])
        {
            low[n]=min(low[n], dfn[to]);
        }
    }
}
void LCA(int a, int b)
{
    while(lev[a]>lev[b])
    {
        if(isbridge[a]) cnt--, isbridge[a]=0;
        a=father[a];
    }
    while(lev[a]<lev[b])
    {
        if(isbridge[b]) cnt--, isbridge[b]=0;
        b=father[b];
    }
    while(a!=b)
    {
        if(isbridge[a]) cnt--, isbridge[a]=0;
        a=father[a];
        if(isbridge[b]) cnt--, isbridge[b]=0;
        b=father[b];
    }
}
int main()
{
    _;
    int Case=0;
    int n;
    while(cin>>n)
    {
        M(father, 0);M(low, 0);M(dfn, 0);M(isbridge, 0);M(lev, 0);
        for(int i=0;i<MAXN;i++) G[i].clear();
        cnt=dfs_clock=0;

        int m;cin>>m;
        if(n==0&&m==0) break;

        cout<<"Case "<<++Case<<":"<<endl;
        while(m--)
        {
            int a, b;cin>>a>>b;
            G[a].push_back(b);G[b].push_back(a);
        }
        for(int i=1;i<=n;i++)
        {
            if(!dfn[i])
                targan(i);
        }

        int q;cin>>q;
        while(q--)
        {
            int a, b;cin>>a>>b;
            LCA(a, b);
            cout<<cnt<<endl;
        }
        cout<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值