欢迎来到下蛋爷之家|

下蛋爷

园龄:4年2个月粉丝:8关注:23

CF1149D Abandoning Roads 题解

Description

一张 n 个点 m 条边的无向图,只有 a,b 两种边权(a<b),对于每个 i,求图中所有的最小生成树中,从 1i 距离的最小值。

2n70,n1m200,1a<b107

Solution

先考虑一个最小生成树是什么样的形态,显然保留边权为 a 的边后形成的连通块和原图保留 a 边的连通块完全相同,并且树中连接连通块之间的边都是 b 边。

所以树上两点的简单路径一定是先走 a 再走 b 再走 a,以此类推,并且如果出了一个连通块就不会再回来,容易发现在原图中如果存在一条这样的 1i 的路径,那么在新树中一定也存在。

这样就可以 dp 了,设 fs,i 表示已经出了 s 这个集合的所有连通块,并且当前在 j 的最短路。跑 dijkstra 即可。

时间复杂度:O(2nn)

考虑优化。

注意到对于一个大小不超过 3 的连通块,如果出了它再走回来一定没有直接走连通块内的边优,所以这些连通块不用记到状态里,则状态数总共就只有 2n4 个了。

时间复杂度:O(2n4n)

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;
}
posted @   下蛋爷  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起