zoukankan      html  css  js  c++  java
  • 【补遗】 Let me sleep(NCD 2019, Gym

    题意

    在一张n个点,m条边的无向图中允许再加一条边,问增加后图中最少还有多少条割边。((1≤N,M≤10^5))

    分析

    要注意这样一个情况,一般求割边在不是和网络流有关的情况下都是和双连通分量有关的。因为是割边,因此我们按照边双连通分量缩点,得到一个森林,此时森林里的每一个边都是割边。
    然后接下来考虑增加边。在森林间的树之间增加边是很憨憨的一个行为,这只会增加桥;于是就是在树上加边。为了减少树上的割边,我们希望能够把树上最长路径的端点连接起来——也就是直径。
    问题于是解决。

    代码

    #include <bits/stdc++.h>
    #define fi first
    #define se second
    #define MP make_pair
    #define MS(x, y) memset(x, y, sizeof(x))
    #define rep(i, a, b) 
      for (repType i = static_cast<repType>(a); i <= static_cast<repType>(b); ++i)
    #define REP(i, a, b) 
      for (repType i = static_cast<repType>(a); i < static_cast<repType>(b); ++i)
    #define per(i, a, b) 
      for (repType i = static_cast<repType>(a); i >= static_cast<repType>(b); --i)
    #define PER(i, a, b) 
      for (repType i = static_cast<repType>(a); i > static_cast<repType>(b); --i)
    #define ALL(x) x.begin(), x.end()
    
    using namespace std;
    using repType = signed;
    using ll = long long;
    using ld = long double;
    using pi = pair<int, int>;
    
    const int MAXN = 100005;
    struct Edge {
      int u, v, w;
      bool bri;
      Edge() = default;
      Edge(int u, int v, int w = 1) : u(u), v(v), w(w), bri(false) {}
    };
    vector<Edge> edges;
    array<vector<int>, MAXN> G, bcc;
    int pre[MAXN], iscut[MAXN], bccno[MAXN], dfs_clk, bcc_cnt, bri_cnt;
    
    void add_edge(int u, int v) {
      edges.emplace_back(Edge(u, v));
      G[u].emplace_back(int(edges.size() - 1));
    }
    
    stack<Edge> S;
    int tarjan(int u, int fa) {
      int lowu = pre[u] = ++dfs_clk;
      int child = 0;
      for (auto i : G[u]) {
        auto &e = edges[i];
        auto v = e.v;
        if (!pre[v]) {
          S.push(e);
          child++;
          int lowv = tarjan(v, u);
          lowu = min(lowu, lowv);
    
          if (lowv > pre[u]) {  // 桥
            bri_cnt++;
            edges[i].bri = edges[i ^ 1].bri = true;
          }
          if (lowv >= pre[u]) {  // 割点
            iscut[u] = true;
            bcc_cnt++;
            bcc[bcc_cnt].clear();  // 从1开始
            for (;;) {
              auto x = S.top();
              S.pop();
              if (bccno[x.u] != bcc_cnt) {
                bcc[bcc_cnt].emplace_back(x.u);
                bccno[x.u] = bcc_cnt;
              }
              if (bccno[x.v] != bcc_cnt) {
                bcc[bcc_cnt].emplace_back(x.v);
                bccno[x.v] = bcc_cnt;
              }
              if (x.u == u && x.v == v) break;
            }
          }
        } else if (pre[v] < pre[u] && v != fa) {
          S.push(e);
          lowu = min(lowu, pre[v]);
        }
      }
      if (fa < 0 && child == 1) iscut[u] = false;
      return lowu;
    }
    
    void find_bcc(int n) {
      MS(pre, 0);
      MS(iscut, 0);
      MS(bccno, 0);
      dfs_clk = bri_cnt = bcc_cnt = 0;
      REP(i, 0, n) {
        if (!pre[i]) tarjan(i, -1);
      }
    }
    
    int nG_cnt, nG_edge_cnt;
    array<int, MAXN> nG_clr;
    array<vector<int>, MAXN> nG;
    array<unordered_set<int>, MAXN> nG_hash;
    array<bool, MAXN> nG_vis;
    array<int, MAXN> nG_dist;
    void dfs1(int x) {  // 将图分成若干个边-双连通分量
      nG_clr[x] = nG_cnt;
      for (auto i : G[x]) {
        auto &e = edges[i];
        if (nG_clr[e.v] != -1 || e.bri) continue;
        dfs1(e.v);
      }
    }
    
    void build_nG() {
      for (auto e : edges) {
        int u = nG_clr[e.u], v = nG_clr[e.v];
        if (u != v && nG_hash[u].find(v) == nG_hash[u].end()) {
          nG_edge_cnt++;
          nG_hash[u].insert(v);
          nG_hash[v].insert(u);
          nG[u].emplace_back(v);
          nG[v].emplace_back(u);
        }
      }
    }
    
    pi dfs2(int x, int fa) {  // 返回从x出发的最深 <距离, 点>
      nG_vis[x] = true;
      if (fa != -1)
        nG_dist[x] = nG_dist[fa] + 1;
      else
        nG_dist[x] = 1;
    
      auto ret = MP(nG_dist[x], x);
      for (auto v : nG[x]) {
        if (v != fa) {
          auto pa = dfs2(v, x);
          if (pa > ret) {
            ret = pa;
          }
        }
      }
      return ret;
    }
    
    void init(int n) {
      edges.clear();
      rep(i, 0, n) {
        G[i].clear();
        nG[i].clear();
        nG_hash[i].clear();
      }
      nG_cnt = nG_edge_cnt = 0;
      fill(ALL(nG_clr), -1);
      fill(ALL(nG_vis), false);
    }
    
    signed main() {
      int T;
      cin >> T;
      while (T--) {
        int n, m;
        cin >> n >> m;
        init(n);
        rep(i, 1, m) {
          int u, v;
          cin >> u >> v;
          add_edge(u, v);
          add_edge(v, u);
        }
        find_bcc(n);
        rep(i, 1, n) {
          if (nG_clr[i] == -1) {
            nG_cnt++;
            dfs1(i);
          }
        }
        build_nG();
        int ans = 0;
        rep(i, 1, nG_cnt) {
          if (!nG_vis[i]) {
            auto pnt = dfs2(i, -1).second;
            ans = max(ans, dfs2(pnt, -1).first - 1);
          }
        }
        cout << nG_edge_cnt - ans << endl;
      }
    
      return 0;
    }
    
  • 相关阅读:
    LaTeX下的表格处理
    accumulate函数用法的坑
    将博客搬至CSDN
    linux; 文件名乱码;问价名出现问号
    vim打开多窗口、多文件之间的切换
    关于ssh-server
    更改Ubuntu gcc、g++默认编译器版本
    Elasticsearch nest实现类似Contains和Like功能
    Redis大幅性能提升之Batch批量读写
    Ext.NET 4.1 系统框架的搭建(后台) 附源码
  • 原文地址:https://www.cnblogs.com/samhx/p/Gym-102163B.html
Copyright © 2011-2022 走看看