hiho 55 连通性·四

问题

http://hihocoder.com/problemset/problem/1190?sid=787105
求点的连通分量

解法

使用tarjan,用堆栈记录边,在割点处弹栈,注意边可能有两次进入堆栈,我们记录边是否已经进入堆栈。

#include <bits/stdc++.h>
using namespace std;
enum{maxn = 20000+5, maxm = 100000+5};
bool visitV[maxn];
bool visitE[maxm];
int eGroup[maxm];
struct E{
    int b;
    int edgeId;
};
vector<E> G[maxn];
int low[maxn];
int dfn[maxn];
int ess[maxm];
int parent[maxn];
int ret;
int n, m;
void dfs(int u)
{
    static int essTop = -1;
    static int count = 0;

    visitV[u] = true;
    dfn[u] = low[u] = ++count;

    for (int i=0; i<G[u].size(); ++i)
    {
        int v = G[u][i].b;
        int eid = G[u][i].edgeId;
        if (visitE[eid]) // 只允许一次入栈
            continue;
        visitE[eid] = true;
        ess[++essTop] = eid;
        if (!visitV[v])
        {
            parent[v] = u;
            dfs(v);
            low[u] = min(low[u], low[v]);
            if (low[v] >= dfn[u])// 找到割点,或者是根节点。弹栈
            {
                ++ret;
                int j = essTop;
                int minId = eid;
                while(ess[j] != eid){
                    minId = min(minId, ess[j--]);
                }
                while(j!= essTop)
                {
                    eGroup[ess[essTop--]] = minId;
                }
                --essTop;
                eGroup[eid] = minId;
            }
        }else if (v != parent[u])
        {
            low[u] = min(low[u], dfn[v]);
        }
    }
}
int main()
{
    scanf("%d %d", &n, &m);
    for (int i=1; i<=m; ++i)
    {
        int a, b;
        scanf("%d %d", &a, &b);
        G[a].push_back(E{b, i});
        G[b].push_back(E{a, i});
    }
    memset(visitV, 0, sizeof(visitV));
    memset(visitE, 0, sizeof(visitE));
    parent[1] = 0;
    ret =0;
    dfs(1);
    printf("%d\n", ret);
    for (int i=1; i<= m;++i)
        printf("%d ", eGroup[i]);
    printf("\n");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值