一.题目
传送门
翻译:
定义无向图中简单环为某几条边构成的最小环(即该环中不再包含更小的环),现给出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;
}