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;
}