CF1149D Abandoning Roads 題解

下蛋爷發表於2024-04-06

Description

一張 \(n\) 個點 \(m\) 條邊的無向圖,只有 \(a,b\) 兩種邊權(\(a<b\)),對於每個 \(i\),求圖中所有的最小生成樹中,從 \(1\)\(i\) 距離的最小值。

\(2\leq n\leq 70,n-1\leq m\leq 200,1\leq a<b\leq 10^7\)

Solution

先考慮一個最小生成樹是什麼樣的形態,顯然保留邊權為 \(a\) 的邊後形成的連通塊和原圖保留 \(a\) 邊的連通塊完全相同,並且樹中連線連通塊之間的邊都是 \(b\) 邊。

所以樹上兩點的簡單路徑一定是先走 \(a\) 再走 \(b\) 再走 \(a\),以此類推,並且如果出了一個連通塊就不會再回來,容易發現在原圖中如果存在一條這樣的 \(1\)\(i\) 的路徑,那麼在新樹中一定也存在。

這樣就可以 dp 了,設 \(f_{s,i}\) 表示已經出了 \(s\) 這個集合的所有連通塊,並且當前在 \(j\) 的最短路。跑 dijkstra 即可。

時間複雜度:\(O(2^nn)\)

考慮最佳化。

注意到對於一個大小不超過 \(3\) 的連通塊,如果出了它再走回來一定沒有直接走連通塊內的邊優,所以這些連通塊不用記到狀態裡,則狀態數總共就只有 \(2^{\frac{n}{4}}\) 個了。

時間複雜度:\(O(2^{\frac{n}{4}}n)\)

Code

#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 75, kMaxS = (1 << 18);

int n, m, a, b, cnt, tot;
int g[kMaxN][kMaxN], id[kMaxN], f[kMaxN][kMaxS];
bool vis[kMaxN];
std::vector<int> vec;

void dfs(int u) {
  vec.emplace_back(u), vis[u] = 1;
  for (int i = 1; i <= n; ++i)
    if (g[u][i] == a && !vis[i])
      dfs(i);
}

void dijkstra() {
  static bool vis[kMaxN][kMaxS] = {0};
  memset(f, 0x3f, sizeof(f));
  std::priority_queue<std::tuple<int, int, int>> q;
  f[1][0] = 0, q.emplace(0, 1, 0);
  for (; !q.empty();) {
    auto [d, i, s] = q.top(); q.pop();
    if (vis[i][s]) continue;
    vis[i][s] = 1;
    for (int j = 1; j <= n; ++j) {
      if (!g[i][j]) continue;
      int t = s;
      if (id[j] < cnt) {
        if (s >> id[j] & 1) continue;
        if (id[j] == id[i]) {
          if (g[i][j] == a && f[j][t] > f[i][s] + g[i][j]) {
            f[j][t] = f[i][s] + g[i][j], q.emplace(-f[j][t], j, t);
          }
        } else {
          if (id[i] < cnt) t |= (1 << id[i]);
          if (f[j][t] > f[i][s] + g[i][j]) {
            f[j][t] = f[i][s] + g[i][j], q.emplace(-f[j][t], j, t);
          }
        }
      } else {
        if (id[j] == id[i]) {
          if (g[i][j] == a && f[j][t] > f[i][s] + g[i][j]) {
            f[j][t] = f[i][s] + g[i][j], q.emplace(-f[j][t], j, t);
          }
        } else {
          if (id[i] < cnt) t |= (1 << id[i]);
          if (f[j][t] > f[i][s] + g[i][j]) {
            f[j][t] = f[i][s] + g[i][j], q.emplace(-f[j][t], j, t);
          }
        }
      }
    }
  }
}

void dickdreamer() {
  std::cin >> n >> m >> a >> b;
  for (int i = 1; i <= m; ++i) {
    int u, v, w;
    std::cin >> u >> v >> w;
    g[u][v] = g[v][u] = w;
  }
  for (int i = 1; i <= n; ++i) {
    if (vis[i]) continue;
    dfs(i);
    if (vec.size() >= 4) {
      for (auto x : vec) id[x] = tot;
      ++cnt, ++tot;
    }
    vec.clear();
  }
  std::fill_n(vis + 1, n, 0);
  for (int i = 1; i <= n; ++i) {
    if (vis[i]) continue;
    dfs(i);
    if (vec.size() <= 3) {
      for (auto x : vec) id[x] = tot;
      ++tot;
    }
    vec.clear();
  }
  dijkstra();
  for (int i = 1; i <= n; ++i)
    std::cout << *std::min_element(f[i], f[i] + (1 << cnt)) << ' ';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}