题解 CF1149D【Abandoning Roads】
看到 ,想到状压 DP。
首先,显然对于一棵最小生成树,每个轻边连通块内部都是一棵树,轻边连通块缩点后点之间的重边也是一棵树。也就是说,缩点后不存在重边组成的环(包括自环),路径一旦离开了一个轻边连通块就再也不会回来了。
于是先洪水填充求出连通块,设共有 个连通块。设 表示已经离开(不能再回到)的连通块集合为 时, 的最短路径。初始时 ,将转移关系的图建出来之后,每个 都视为一个节点,原图上的每条边被拆为 条边,从源点 出发的单源最短路即为 数组的值。
因此,复杂度是在 个点、 条边的图上跑 Dijkstra 算法的复杂度,最坏 时为 。
注意到当连通块大小不超过 时,内部一定可以通过不超过 条轻边到达,而出连通块再回来至少要走 条重边,这种情况自然不优,我们可以不用处理所有大小不超过 的连通块。因此,我们要处理的连通块数 。
时间复杂度变为 ,可以通过本题。代码想清楚还是挺好写的。
边权只有两种,可以开两个队列进行 BFS 优化成 ,但我没有写。甚至还有 的解法。
//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=(y);x<=(z);x++)
#define per(x,y,z) for(int x=(y);x>=(z);x--)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;
mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
int randint(int L, int R) {
uniform_int_distribution<int> dist(L, R);
return dist(rnd);
}
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
const int N = 75, M = 205, K = 18;
int n, m, a, b, e[N][N], vis1[N], vis2[N], id[N], sz[N], cnt, dim, dp[N][1<<K], vis[N][1<<K];
int dfs1(int u) {
vis1[u] = 1;
int sz = 1;
rep(v, 1, n) if(e[u][v] == a && !vis1[v]) sz += dfs1(v);
return sz;
}
void dfs2(int u, int cnt) {
vis2[u] = 1;
id[u] = cnt;
++sz[cnt];
rep(v, 1, n) if(e[u][v] == a && !vis2[v]) dfs2(v, cnt);
}
int main() {
scanf("%d%d%d%d", &n, &m, &a, &b);
rep(i, 1, m) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
e[u][v] = e[v][u] = w;
}
rep(i, 1, n) if(!vis1[i]) if(dfs1(i) >= 4) dfs2(i, cnt++);
dim = cnt;
memset(vis1, 0, sizeof(vis1));
rep(i, 1, n) if(!vis1[i]) if(dfs1(i) < 4) dfs2(i, cnt++);
memset(dp, 0x3f, sizeof(dp));
priority_queue<tuple<int, int, int>> q;
dp[1][0] = 0;
q.emplace(0, 1, 0);
while(!q.empty()) {
int _, u, S;
tie(_, u, S) = q.top(); q.pop();
if(vis[u][S]) continue;
vis[u][S] = 1;
rep(v, 1, n) {
if(!e[u][v]) continue;
int Sn = S;
if(e[u][v] == b) {
if(id[u] == id[v] || id[v] < dim && ((S >> id[v]) & 1)) continue;
if(id[u] < dim) Sn |= 1 << id[u];
}
if(dp[v][Sn] > dp[u][S] + e[u][v]) {
dp[v][Sn] = dp[u][S] + e[u][v];
q.emplace(-dp[v][Sn], v, Sn);
}
}
}
rep(i, 1, n) {
printf("%d%c", *min_element(dp[i], dp[i]+(1<<dim)), " \n"[i==n]);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现