给定一个连通无向图,求有多少条边仅被包含在一个简单环内并输出
(n, mleq10^5)
tarjan
首先,一个连通块是一个环,当且仅当该连通块的 点数=边数
可以发现,如果两个环仅由一个公共点连接,那么这两个环互不影响,即点双两两互不影响。
所以我们可以考虑处理出点双和每个点双内的边数
但是求出点双后暴力dfs会被如下数据卡掉:
66667 99999
1 2
1 3
2 3
1 4
1 5
4 5
1 6
1 7
6 7
...
因为 (1) 节点每次枚举所有边的效率太低
于是可以在tarjan时将所有边压入栈中,再用set统计点双中的边数以及答案并去重
时间复杂度 (O(n+m))
代码
#include <bits/stdc++.h>
using namespace std;
#define nc getchar()
const int maxn = 1e5 + 10;
int n, m, tot, top, h[maxn], dfn[maxn], low[maxn], st[maxn * 3];
struct edges {
int nxt, to;
} e[maxn << 1];
set <int> ans, edge[maxn], node[maxn];
inline int read() {
int x = 0; char c = nc;
while (c < 48) c = nc;
while (c > 47) x = x * 10 + c - 48, c = nc;
return x;
}
void addline(int u, int v) {
static int cnt = 1;
e[++cnt] = edges{h[u], v}, h[u] = cnt;
}
void tarjan(int u, int f) {
static int now;
dfn[u] = low[u] = ++now;
for (int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (!dfn[v]) {
st[++top] = i >> 1, st[++top] = u, st[++top] = v;
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (dfn[u] <= low[v]) {
tot++;
while (1) {
int t1, t2;
node[tot].insert(t1 = st[top--]);
node[tot].insert(t2 = st[top--]);
edge[tot].insert(st[top--]);
if (t1 == v && t2 == u) break;
}
}
} else if (dfn[v] < dfn[u] && v != f) {
st[++top] = i >> 1, st[++top] = u, st[++top] = v;
low[u] = min(low[u], dfn[v]);
}
}
}
int main() {
n = read(), m = read();
for (int i = 1; i <= m; i++) {
int u = read(), v = read();
addline(u, v), addline(v, u);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) tarjan(i, 0);
}
for (int i = 1; i <= tot; i++) {
if (edge[i].size() == node[i].size()) {
ans.insert(edge[i].begin(), edge[i].end());
}
}
printf("%d
", (int)ans.size());
for (int u : ans) printf("%d ", u);
return 0;
}