CF962F Simple Cycles Edges

CF962F Simple Cycles Edges

给定一个连通无向图,求有多少条边仅被包含在一个简单环内并输出

(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;
}
原文地址:https://www.cnblogs.com/Juanzhang/p/10374869.html