[bzoj3669][Noi2014]魔法森林
题目大意:有一张$n$个点,$m$条边的无向图,第$i$条边有权值$a_i,b_i$,求一条$1$到$n$的路径,使得这条路径上$\max\{a_i\}+\max\{b_i\}$最小
题解:先想到二分,然而两个权值,有没有关键字先后顺序的二分我并不会。。。
然后发现,可以先按$a_i$把每条边排序,一条一条加入。若一条边的端点原来就已经连接,就找到原来路径上$\max\{b_i\}$,把这条边删去,再加上这条新的边。如果这时$1->n$已经连通,就更新答案。$LCT$
可以用$LCT$来维护路径最大值即可
卡点:$access$时断重边断错
C++ Code:
#include <cstdio> #include <algorithm> #define maxn 50010 #define maxm 100010 #define N maxn + maxm #define lc(x) son[x][0] #define rc(x) son[x][1] const int inf = 0x3f3f3f3f; int n, m, ans = inf; int fa[N], son[N][2], tg[N], mx[N]; int w[N]; inline int get(int rt) {return son[fa[rt]][1] == rt;} inline bool isrt(int rt) {return lc(fa[rt]) != rt && rc(fa[rt]) != rt;} inline void swap(int &a, int &b) {a ^= b ^= a ^= b;} inline void swap(int rt) {swap(lc(rt), rc(rt));}; inline void pushdown(int rt) { swap(rt), tg[rt] ^= 1; tg[lc(rt)] ^= 1, tg[rc(rt)] ^= 1; } inline int gmax(int a, int b) {return w[a] > w[b] ? a : b;} inline void pushup(int rt) { mx[rt] = rt; if (lc(rt)) mx[rt] = gmax(rt, mx[lc(rt)]); if (rc(rt)) mx[rt] = gmax(mx[rt], mx[rc(rt)]); } inline void rotate(int x) { int y = fa[x], z = fa[y], b = get(x); if (!isrt(y)) son[z][get(y)] = x; fa[son[y][b] = son[x][!b]] = y; son[x][!b] = y; fa[y] = x; fa[x] = z; pushup(x), pushup(y); } int S[N], top; inline void splay(int x) { S[top = 1] = x; for (int y = x; !isrt(y); S[++top] = y = fa[y]); for (; top; top--) if (tg[S[top]]) pushdown(S[top]); for (; !isrt(x); rotate(x)) if (!isrt(fa[x])) get(x) ^ get(fa[x]) ? rotate(x) : rotate(fa[x]); pushup(x); } inline void access(int rt) {for (int t = 0; rt; rc(rt) = t, t = rt, rt = fa[rt]) splay(rt);} inline void mkrt(int rt) {access(rt), splay(rt), tg[rt] ^= 1;} inline void link(int x, int y) {mkrt(x), fa[x] = y;} inline void split(int x, int y) {mkrt(x), access(y), splay(y);} inline void cut(int x, int y) {split(x, y), lc(y) = fa[x] = 0;} inline int getmax(int x, int y) {split(x, y); return mx[y];} inline bool connect(int x, int y) { split(x, y); int now = y; while (lc(now)) now = lc(now); return now == x; } struct Edge { int u, v, a, b; inline bool operator < (const Edge &rhs) const {return a < rhs.a;} } e[maxm]; inline int min(int a, int b) {return a < b ? a : b;} int main() { scanf("%d%d", &n, &m); for (int i = 0; i < m; i++) { scanf("%d%d%d%d", &e[i].u, &e[i].v, &e[i].a, &e[i].b); } std::sort(e, e + m); for (int i = 0; i < m; i++) { int u = e[i].u, v = e[i].v, t = i + n + 1; w[t] = e[i].b; if (!connect(u, v)) link(u, t), link(v, t); else { int pos = getmax(u, v); if (w[pos] > e[i].b) { cut(e[pos - n - 1].u, pos), cut(e[pos - n - 1].v, pos); link(u, t), link(v, t); } } if (connect(1, n)) ans = min(ans, e[i].a + w[getmax(1, n)]); } if (ans == inf) puts("-1"); else printf("%d\n", ans); return 0; }