【ybt金牌导航5-4-3】【luogu P2387】魔法森林

魔法森林

题目链接:ybt金牌导航5-4-3 / luogu P2387

题目大意

有一个无向图,每个边有 AB 两种权值。
要找一条从 1 到 n 的路径,使得路径上的边 A 权值最大值加 B 权值最大值最小。
如果无法到达输出 -1。

思路

首先看到两个权值的最大最小,然后每个范围都不是很大。

我们考虑枚举一个的最大值,然后把边按哪个排序,构出每个时刻的图,然后让另一个的最大值最小。
那接着问题就是如果让最大值最小了。
发现要不断的加边然后维护这个值,我们想到 LCT。
但是考虑权值在边上,而且它不是树。
我们考虑用这么一个搞法,它边有权,我们就把边看做 LCT 的点。
然后拿并查集维护点之间的连通性,如果之前不连通,就正常搞。
否则就 LCT 提取出那条链,如果它的最大值大于你的现在这条,肯定就是换(把之前的删掉再加边),否则就不管。

然后就 LCT 维护一下就可以了。

代码

#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define INF 0x3f3f3f3f3f3f3f3f using namespace std; struct line { int x, y, a, b; }a[150001]; int n, m, le[150001], KK, ans = INF; int nowa, fa[150001], dist[150001]; int l[150001], r[150001], pl[150001]; int fath[150001]; bool lzs[150001], ch; bool cmp(line x, line y) {//按a排序 return x.a < y.a; } bool nrt(int x) {//LCT return l[fa[x]] == x || r[fa[x]] == x; } bool ls(int x) { return l[fa[x]] == x; } void up(int x) { pl[x] = x;//记得要清空,并算自己的 if (l[x] && dist[pl[x]] < dist[pl[l[x]]]) pl[x] = pl[l[x]]; if (r[x] && dist[pl[x]] < dist[pl[r[x]]]) pl[x] = pl[r[x]]; } void downs(int x) { swap(l[x], r[x]); lzs[x] ^= 1; } void down(int x) { if (lzs[x]) { if (l[x]) downs(l[x]); if (r[x]) downs(r[x]); lzs[x] = 0; } } void down_line(int x) { if (nrt(x)) down_line(fa[x]); down(x); } void rotate(int x) { int y = fa[x]; int z = fa[y]; int b = (ls(x) ? r[x] : l[x]); if (z && nrt(y)) (ls(y) ? l[z] : r[z]) = x; if (ls(x)) r[x] = y, l[y] = b; else l[x] = y, r[y] = b; fa[x] = z; fa[y] = x; if (b) fa[b] = y; up(y); } void Splay(int x) { down_line(x); while (nrt(x)) { if (nrt(fa[x])) { if (ls(x) == ls(fa[x])) rotate(fa[x]); else rotate(x); } rotate(x); } up(x); } void access(int x) { int lst = 0; for (; x; x = fa[x]) { Splay(x); r[x] = lst; up(x); lst = x; } } void make_root(int x) { access(x); Splay(x); downs(x); } int find_root(int x) { access(x); Splay(x); while (l[x]) { down(x); x = l[x]; } Splay(x); return x; } void cut(int x, int y) { make_root(x); access(y); Splay(y); l[y] = 0; fa[x] = 0; up(y); } void link(int x, int y) { make_root(x); if (find_root(y) != x) { fa[x] = y; } } int split(int x, int y) { make_root(x); access(y); Splay(y); return y; } int query(int x, int y) { x = split(x, y); return dist[pl[x]]; } int find(int now) {//并查集维护连通 if (fath[now] == now) return now; return fath[now] = find(fath[now]); } void connect(int x, int y) { int X = find(x); int Y = find(y); fath[X] = Y; } bool connect_ck(int x, int y) { int X = find(x); int Y = find(y); return X == Y; } int main() { scanf("%d %d", &n, &m); for (int i = 1; i <= m; i++) { scanf("%d %d %d %d", &a[i].x, &a[i].y, &a[i].a, &a[i].b); if (a[i].x == a[i].y) i--, m--; } sort(a + 1, a + m + 1, cmp); for (int i = 1; i <= n + m; i++) pl[i] = i, fath[i] = i; for (int i = n + 1; i <= n + m; i++) dist[i] = a[i - n].b; for (int i = 1; i <= 50000; i++) {//枚举最大的 a ch = 0; while (nowa < m && a[nowa + 1].a <= i) {//逐渐加边 nowa++; if (connect_ck(a[nowa].x, a[nowa].y)) {//加边会出环,判断哪个更优 int w = pl[split(a[nowa].x, a[nowa].y)]; if (dist[w] > a[nowa].b) {//更优(把之前的边删了,再连) cut(a[w - n].x, w); cut(w, a[w - n].y); link(a[nowa].x, n + nowa); link(n + nowa, a[nowa].y); } } else link(a[nowa].x, n + nowa), link(n + nowa, a[nowa].y), connect(a[nowa].x, a[nowa].y);//普通连边 ch = 1; } if (ch && connect_ck(1, n)) { ans = min(ans, i + query(1, n)); if (nowa == m) break; } } if (ans == (int)INF) printf("-1"); else printf("%d", ans); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_5-4-3.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(41)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示