Luogu2860 [USACO06JAN]冗余路径Redundant Paths

Luogu2860 [USACO06JAN]冗余路径Redundant Paths

给定一个连通无向图,求至少加多少条边才能使得原图变为边双连通分量

\(1\leq n\leq5000,\ n-1\leq m\leq10^4\)

tarjan


边双无疑不用考虑,于是就可以边双缩点成一棵树

令现在要连的边为 \((u,\ v)\) ,当前树上 \(bl_u\)\(bl_v\) 的链将会变为一个新的点双,可以将他们看为一个新的点

可以贪心地连边使得每次连边后,不复存在的点尽量多,当只剩一个点时,原图就变成了一个双连通分量

如果 \(u\) 为非叶节点,显然不如将 \(u\) 子树中的一点 \(u'\)\(v\) 连接,于是 \(u,\ v\) 均为叶节点

\(lca(u,\ v)\)\(root\) ,将会消去两个叶节点,否则只会消去一个叶节点,因此每次选择 \(lca(u,\ v)\)\(root\) 的两个点,答案即为 \(叶节点的个数\lfloor\frac{\verb|叶节点的个数|+1}{2}\rfloor\)

时间复杂度 \(O(n+m)\)

代码

#include <bits/stdc++.h>
using namespace std;

#define nc getchar()
const int maxn = 5010;
int n, m, tot, h[maxn], bl[maxn], dfn[maxn], low[maxn], deg[maxn]; bool vis[maxn], cut[maxn << 1];
struct edges {
  int nxt, to;
  edges(int x = 0, int y = 0) : nxt(x), to(y) {}
} e[maxn << 1];

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]) {
      tarjan(v, u);
      low[u] = min(low[u], low[v]);
      if (dfn[u] < low[v]) cut[i] = cut[i ^ 1] = 1;
    } else if (v != f) {
      low[u] = min(low[u], dfn[v]);
    }
  }
}

void dfs(int u) {
  vis[u] = 1, bl[u] = tot;
  for (int i = h[u]; i; i = e[i].nxt) {
    int v = e[i].to;
    if (!cut[i] && !vis[v]) dfs(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);
  }
  tarjan(1, 0);
  for (int i = 1; i <= n; i++) {
    if (!vis[i]) tot++, dfs(i);
  }
  for (int u = 1; u <= n; u++) {
    for (int i = h[u]; i; i = e[i].nxt) {
      int v = e[i].to;
      if (bl[u] != bl[v]) deg[bl[v]]++;
    }
  }
  int ans = 0;
  for (int i = 1; i <= tot; i++) {
    ans += deg[i] == 1;
  }
  printf("%d", (ans + 1) >> 1);
  return 0;
}

转载于:https://www.cnblogs.com/Juanzhang/p/10375244.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值