CF962F Simple Cycles Edges 点双连通分量+缩点

一.题目

传送门
翻译:
定义无向图中简单环为某几条边构成的最小环(即该环中不再包含更小的环),现给出n点m条边(n,m<=100000),求恰好被包含在一个(多了少了都不行)简单环中的边,第一行输出这些边个数,第二行根据输入顺序输出这些边编号.数据保证无重边自环,但不一定保证连通.

二.题解

这道题目最关键的一点是理解简单环

注意:如果一个点双连通分量内的点数等于边数,那么这就是一个简单环

理解了这一点就好办了,只需将原图按点双连通分量缩点,一边缩点一边统计点双连通分量内点和边的数量,并把点和边用vector存下来,最后把点和边数量相等的连通块输出就行了。

三.Code

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
#include <map>
using namespace std;

#define M 100005

struct node {
    int u, v;
    node (){};
    node (int U, int V){
        u = U;
        v = V;
    }
};
int n, m, dfn[M], low[M], cnt, num, ans[M], len, belong[M];
vector <int> G[M];
vector <int> d[M];
vector <int> b[M];
map <int, int> e[M];
stack <node> s;

void Tarjan (int u, int fa){
    dfn[u] = low[u] = ++ cnt;
    int child = 0;
    for (int i = 0; i < G[u].size (); i ++){
        int v = G[u][i];
        if (v == fa)
            continue;
        if (! dfn[v]){
            s.push (node (u, v));
            child ++;
            Tarjan (v, u);
            low[u] = min (low[v], low[u]);
            if (low[v] >= dfn[u]){
                num ++;
                while (1){
                    node now = s.top ();
                    s.pop ();
                    if (belong[now.u] != num){belong[now.u] = num, d[num].push_back (now.u);}
                    if (belong[now.v] != num){belong[now.v] = num, d[num].push_back (now.v);}
                    b[num].push_back (e[now.u][now.v]);
                    if (u == now.u && v == now.v)
                        break;
                }
            }
        }
        else if (dfn[v] < dfn[u]){
            s.push(node (u, v));
            low[u] = min (low[u], dfn[v]);
        }
    }
}
int main (){
    scanf ("%d %d", &n, &m);
    for (int i = 1; i <= m; i ++){
        int u, v;
        scanf ("%d %d", &u, &v);
        G[u].push_back (v);
        G[v].push_back (u);
        e[u][v] = e[v][u] = i;
    }
    for (int i = 1; i <= n; i ++){
        if (! dfn[i])
            Tarjan (i, 0);
    }
    for (int i = 1; i <= num; i ++){
        if (b[i].size() == d[i].size()){
            for (int j = 0; j < b[i].size(); j ++)
                ans[++ len] = b[i][j];
        }
    }
    sort (ans + 1, ans + 1 + len);
    len = unique (ans + 1, ans + 1 + len) - ans - 1;
    printf ("%d\n", len);
    for (int i = 1; i <= len; i ++)
        printf ("%d ", ans[i]);
    return 0;
}

Thanks!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值