[BZOJ 3669] [Noi2014] 魔法森林 【LCT】
题目链接:BZOJ - 3669
题目分析
如果确定了带 x 只精灵A,那么我们就是要找一条 1 到 n 的路径,满足只经过 Ai <= x 的边,而且要使经过的边中最大的 Bi 尽量小。
其实就是一个按照 Bi 建立的 MST 上 1 到 n 的路径。只能使用 Ai <= x 的边。
那么,如果我们从小到大枚举 x ,这样可以使用的边就不断增加,就是在加边的同时维护 MST ,用 LCT 来做就可以了。
如果新加入一条边 (u, v, w) ,并且原 MST 上 u 到 v 的路径中边权最大的边的边权大于 w ,那么就删掉那条边权最大的边,然后把这条新加入的边连到 MST 中。
备注:在 Cut 不够优的边时,我之前将代码写出了非常可怕的 BUG ,但是由于神奇的写法的巧合,这个 BUG 并没有造成后果,我用错误的代码 AC 了两道题..
现在下方的代码是正确的了。
代码
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> using namespace std; inline void Read(int &Num) { char c = getchar(); while (c < '0' || c > '9') c = getchar(); Num = c - '0'; c = getchar(); while (c >= '0' && c <= '9') { Num = Num * 10 + c - '0'; c = getchar(); } } inline int gmax(int a, int b) {return a > b ? a : b;} inline int gmin(int a, int b) {return a < b ? a : b;} const int MaxN = 50000 + 5, MaxM = 100000 + 5, MaxT = 150000 + 5, INF = 999999999; int n, m, Ans; int Father[MaxT], Son[MaxT][2], V[MaxT], T[MaxT]; bool isRoot[MaxT], Rev[MaxT]; struct ES { int u, v, p, q; } E[MaxM]; inline bool Cmp(ES e1, ES e2) { return e1.p < e2.p; } /**************************** LCT Start *******************************/ inline int Tmax(int a, int b) {return V[a] > V[b] ? a : b;} inline void Update(int x) { T[x] = Tmax(x, Tmax(T[Son[x][0]], T[Son[x][1]])); } inline void Reverse(int x) { Rev[x] = !Rev[x]; swap(Son[x][0], Son[x][1]); } inline void PushDown(int x) { if (!Rev[x]) return; Rev[x] = false; if (Son[x][0]) Reverse(Son[x][0]); if (Son[x][1]) Reverse(Son[x][1]); } inline int GetDir(int x) { if (x == Son[Father[x]][0]) return 0; else return 1; } void Rotate(int x) { int y = Father[x], f; PushDown(y); PushDown(x); f = GetDir(x) ^ 1; if (isRoot[y]) { isRoot[y] = false; isRoot[x] = true; } else { if (y == Son[Father[y]][0]) Son[Father[y]][0] = x; else Son[Father[y]][1] = x; } Father[x] = Father[y]; Son[y][f ^ 1] = Son[x][f]; if (Son[x][f]) Father[Son[x][f]] = y; Son[x][f] = y; Father[y] = x; Update(y); Update(x); } void Splay(int x) { int y; while (!isRoot[x]) { y = Father[x]; if (isRoot[y]) { Rotate(x); break; } if (GetDir(y) == GetDir(x)) Rotate(y); else Rotate(x); Rotate(x); } } int Access(int x) { int y = 0; while (x != 0) { Splay(x); PushDown(x); if (Son[x][1]) isRoot[Son[x][1]] = true; Son[x][1] = y; if (y) isRoot[y] = false; Update(x); y = x; x = Father[x]; } return y; } inline void Make_Root(int x) { int t = Access(x); Reverse(t); } inline void Link(int x, int y) { Make_Root(x); Splay(x); Father[x] = y; } inline void Cut(int x, int y) { Make_Root(x); Access(y); Splay(y); PushDown(y); isRoot[Son[y][0]] = true; Father[Son[y][0]] = 0; Son[y][0] = 0; Update(y); } inline int Find_Root(int x) { int t = Access(x); while (Son[t][0] != 0) t = Son[t][0]; return t; } /**************************** LCT End *******************************/ int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= m; ++i) { Read(E[i].u); Read(E[i].v); Read(E[i].p); Read(E[i].q); } sort(E + 1, E + m + 1, Cmp); // by ES.p for (int i = 1; i <= m; ++i) V[n + i] = E[i].q; for (int i = 1; i <= n + m; ++i) { isRoot[i] = true; Father[i] = 0; T[i] = i; } Ans = INF; int t, CutE; for (int i = 1; i <= m; ++i) { if (Find_Root(E[i].u) != Find_Root(E[i].v)) { Link(E[i].u, n + i); Link(E[i].v, n + i); } else { Make_Root(E[i].u); t = Access(E[i].v); if (V[T[t]] > E[i].q) { CutE = T[t]; Cut(CutE, E[CutE - n].u); Cut(CutE, E[CutE - n].v); Link(E[i].u, n + i); Link(E[i].v, n + i); } } if (Find_Root(1) == Find_Root(n)) { Make_Root(1); t = Access(n); Ans = gmin(Ans, E[i].p + V[T[t]]); } } if (Ans == INF) Ans = -1; printf("%d\n", Ans); return 0; }