POJ 3695 nework (Tarjan的并查集变形)

题意:一棵无向连通图,存在重边,q次操作每次加一条边,问每次加边后的割边数。

解析:最朴实的思想是缩点然后对树上不断求lca,虽然可以但是有点麻烦。可以把low数组的含义改变下,不再存dfn_cnt的最小值,而是可以到达的dfn最小点的下标,这样实际上跑一遍tarjan就可以求出一个用并查集维护好的树。这样修改时只需要不断向上合并父节点就可以了,感觉这个思路很巧妙,是对于tarjan思想的一种变形改造。

代码:

#include <cmath> //定义数学函数
#include <cstdio> //定义输入/输出函数
#include <cstdlib> //定义杂项函数及内存分配函数
#include <cstring> //字符串处理
#include <algorithm> //STL 通用算法
#include <cmath>
#include <list> //STL 线性列表容器
#include <map> //STL 映射容器
#include <iostream>
#include <queue> //STL 队列容器
#include <set> //STL 集合容器#
#include <stack> //STL 堆栈容器
#include <string> //字符串类
#include <vector> //STL 动态数组容器
#include <sstream>
#define x first
#define y second
#define mid (l+r>>1)
#define lo (o<<1)
#define ro (o<<1|1)
using namespace std;
typedef long long ll;
typedef vector<int>vi;
typedef pair<int,int>pii;
struct tri{int x,y,z;};
const int inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;
const int N=1e5+10;
const ll mod=1e9+7;
const double PI=acos(0)*2;

int n,m,dfn_cnt,dfn[N],low[N],fa[N],dep[N];
vi edg[N];
int Min(int a,int b)
{
    if(dfn[a]<dfn[b])return a;
    else return b;
}
void tarjan(int u)
{
    dfn[u]=++dfn_cnt;
    low[u]=u;
    bool flag=0;
    for(int i=0;i<edg[u].size();i++)
    {
        int v=edg[u][i];
        if(v==fa[u]&&!flag)
        {
            flag=1;
            continue;
        }

        if(!dfn[v])
        {
            dep[v]=dep[u]+1;
            fa[v]=u;
            tarjan(v);
        }
        low[u]=Min(low[u],low[v]);
    }
}
int ff(int a)
{
    return a==low[a]?a:low[a]=ff(low[a]);
}
void solve()
{
    for(int i=1;i<=n;i++)edg[i].clear();
    memset(dfn+1,0,sizeof(*dfn)*n);
    dfn_cnt=0;
    while(m--)
    {
        int a,b;
        cin>>a>>b;
        edg[a].push_back(b);
        edg[b].push_back(a);
    }
    tarjan(1);
    int ans=-1;
    for(int i=1;i<=n;i++)if(ff(i)==i)ans++;

    int q;cin>>q;
    while(q--)
    {
        int a,b;cin>>a>>b;
        a=ff(a),b=ff(b);
        while(a!=b)
        {
            if(dep[a]>dep[b])swap(a,b);
            low[b]=fa[b];
            b=ff(b);
            ans--;
        }
        cout<<ans<<endl;
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
//    freopen("in.txt","r",stdin);
    for(int cas=1;;cas++)
    {
        cin>>n>>m;
        if(!n&&!m)return 0;
        cout<<"Case "<<cas<<":"<<endl;
        solve();
        cout<<endl;
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值