qoj18375 Graph

给定图 \(G\),最小化删去边 \((s,t)\)\((u,v)\) 后得到的图 \(G'\)\(d'(s,t)+d'(u,v)\) 的最小值,其中 \(d'(x,y)\) 是点 \(x,y\) 在图 \(G'\) 上的距离。并计数取得最小值的方案数。

\[n,m\le 5\times 10^3 \]


考虑只删一条边,答案为最小环的大小 \(-1\)

同理,可得原问题的答案即为最小环的大小加次小环的大小 \(-2\)

证明考虑,首先其显然为答案下界。

其次设最小环和次小环为 \(R_1,R_2\),删去的边 \(E_1\in R_1,E_2\in R_2\)

若删去 \(E_1\) 使得 \(E_2\) 的贡献无法取得最小值 \(|R_2|-1\),必有 \(E_1\in R_2\)

若对任意 \(E_1\) 均成立,必有 \(R_1\subset R_2\),而这对于简单环是不可能的。


考虑计数,我们首先枚举每条边 \(i\),找出包含该边的最小环 \(R_i\)

对每条边 \(j\) 考虑。\(j\)\(i\) 能否同时被选择,当且仅当存在一个环 \(R'\) 使得 \(|R'|=R_i\)\(i\in R',j\notin R'\)

为了快速断言这一点,我们对环 \(r'\) 计数,以及对包含边 \(j\)\(r'\) 计数,通过维护两点间最短路的方案数不难实现。

取喜欢的模数哈希即可做到几乎正确判定。

最后枚举边 \(i,j\) 满足 \(|R_i|+|R_j|\) 取得全局最小值,且 \(i,j\) 可以同时取得。

时间复杂的 \(\mathcal O(n^2)\)


#include <algorithm>
#include <iostream>
#include <vector>
#include <random>
#include <queue>
#include <unordered_set>

const int O = 1069313197;
auto incr = [](auto& x, auto&& y) { x += y, x -= (x >= O) * O; };
typedef unsigned long long u64;

u64 weight[5000];
inline void init() {
  std::mt19937_64 rnd(5201314);
  for(int i = 0; i < 5000; ++i) weight[i] = rnd();
}

inline void solve() {
  int n, m;
  std::cin >> n >> m;
  std::vector<std::vector<int>> g(n);

  struct st { int x, y, w; std::vector<bool> T; };
  std::vector<st> edg(m);
  for(int x, y, i = 0; i < m; ++i) {
    std::cin >> x >> y, --x, --y;
    g[x].emplace_back(y);
    g[y].emplace_back(x);
    edg[i].x = x, edg[i].y = y;
  }

  std::vector<int> ban(m);
  std::unordered_set<u64> set;

  int ans1 = 1e9, ans2 = 1e9;
  for(int i = 0; i < m; ++i) {
    auto& [x, y, w, T] = edg[i];

    auto bfs = [&](int s, std::vector<int>& d, std::vector<int>& p) {
      std::queue<int> q;
      q.push(s), d[s] = 0, p[s] = 1;
      while(!q.empty()) {
        int u = q.front(); q.pop();
        for(auto& v: g[u]) {
          if((v==x && u==y) || (v==y && u==x)) continue;
          if(d[v] == -1) d[v] = d[u] + 1, q.push(v);
          if(d[v] == d[u] + 1) incr(p[v], p[u]);
        }
      }
    };

    std::vector<int> dx(n, -1), dy(n, -1), px(n), py(n);
    bfs(x, dx, px), bfs(y, dy, py);

    w = dx[y]; int p = px[y];

    if(w == -1 || w > ans2) {
      ban[i] = 1;
      continue;
    }

    T.resize(m);

    u64 hash = weight[i];
    for(int j = 0; j < m; ++j) {
      auto& [a, b, $1, $2] = edg[j];
      if(j == i) continue;
      T[j] = 1;
      if((dx[a] + dy[b] + 1 == w && 1ll * px[a] * py[b] %O == p) ||
         (dx[b] + dy[a] + 1 == w && 1ll * px[b] * py[a] %O == p))
        T[j] = 0, hash ^= weight[j];
    }

    if(p > 1) {
      ans1 = std::min(ans1, w);
      ans2 = std::min(ans2, w);
    } else if(!set.count(hash)) {
      set.insert(hash);
      if(w < ans1) ans2 = ans1, ans1 = w;
      else if(w < ans2) ans2 = w;
    }
  }

  int ans = 0;
  for(int i = 0; i < m; ++i) {
    if(ban[i] || edg[i].w > ans2) continue;
    for(int j = i+1; j < m; ++j) {
      if(ban[j]) continue;
      if(edg[i].w + edg[j].w == ans1 + ans2 && edg[i].T[j] && edg[j].T[i]) {
        ++ans;
      }
    }
  }

  std::cout << ans1 + ans2 << ' ' << ans << "\n";
}

int main() {
  init(), std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int t; std::cin >> t; while(t--) solve();
}
posted @ 2026-08-22 23:27  CuteNess  阅读(16)  评论(0)    收藏  举报