「Bzoj 3669」「NOI2014」魔法森林 (LCT动态维护最小生成树)

BZOJ 3669
题意:给定$n$点$m$边无向图,每条边有一个边权$(a, b)$,能经过某条边当且仅当当前的$A \leq a, B \leq b$,试确定$A,B$,使得能从$1$走到$n$。

第一眼以为二分套二分,但是这题二分后再二分没有单调性了…但是还是能拿到$65$分
贪心两者和最小:将前者排序后,在不大于前者的情况下,让后者尽量小。
那么将边按照$a$排序,然后我们想想怎么样能让$b$最小。
显然一条路径最长边最小的路径在当前图的最小瓶颈生成树上,那么这里就是动态维护一个最小生成树

即,若两点不连通,则直接连边;否则将路径上的最大边删除加入这条边(或者这条边比较小不用动任何东西)

那么 LCT 维护路径。注意重边和自环。

这里边权可以变成点权,是个经典套路

注意Splay后所有的信息都不能再用之前的,变量改变后不要再用,要留意变量是否会在中途改变,会的话开临时数组存储 (特别是用了一个值之前调用了一个函数,这个函数可能改变这个值)!!!

知识点:
1、边权变点权 (网络流常用技巧)
2、Splay后所有的信息都不能再用之前的,变量改变后不要再用,要留意变量是否会在中途改变,会的话开临时数组存储 (特别是用了一个值之前调用了一个函数,这个函数可能改变这个值)
3、贪心两者和最小:将前者排序后,在不大于前者的情况下,让后者尽量小。
4、注意重边自环

#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<vector>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db long double
#define fir first
#define sec second
#define mp make_pair
using namespace std;

namespace flyinthesky {

    #define lc ch[x][0]
    #define rc ch[x][1]

    const int MAXN = 200000 + 5, INF = 1000000000;

    struct edge {
        int x, y, a, b;
        bool operator < (const edge &rhs) const {
            return a == rhs.a ? b < rhs.b : a < rhs.a;
        }
    } ed[100000 + 5];

    int n, m;
    int val[MAXN], ch[MAXN][2], fa[MAXN], rev[MAXN], maxv[MAXN], id[MAXN];
    int top, stk[MAXN];

    int isRt(int x) {return ch[fa[x]][0] != x && ch[fa[x]][1] != x;} //???Splay??? 
    int rel(int x) {return ch[fa[x]][1] == x;} // ? Splay
    void prorev(int x) {swap(lc, rc), rev[x] ^= 1;} // ?????? (????) 
    void pushup(int x) {
        maxv[x] = val[x], id[x] = x;
        if (maxv[x] < maxv[lc])
            maxv[x] = maxv[lc], id[x] = id[lc];
        if (maxv[x] < maxv[rc]) 
            maxv[x] = maxv[rc], id[x] = id[rc];
    }
    void pushdown(int x) { //????
        if (rev[x]) {
            if (lc) prorev(lc);
            if (rc) prorev(rc);
            rev[x] = 0;
        }
    }
    void rotate(int x) { // ? Splay
        pushdown(fa[x]), pushdown(x);
        int y = fa[x], z = fa[y], k = rel(x), w = ch[x][k ^ 1];
        if (!isRt(y)) ch[z][rel(y)] = x;
        fa[x] = z; // y ??? (z, y) ???,???? (??? Splay ???? 1) 
        ch[y][k] = w, fa[w] = y;
        ch[x][k ^ 1] = y, fa[y] = x;
        pushup(y), pushup(x);
    }
    void splay(int x) { // ? Splay
        //printf("%d\n", x);
        top = 0;
        stk[++top] = x;
        for (int pos = x; !isRt(pos); pos = fa[pos]) stk[++top] = fa[pos];
        while (top) pushdown(stk[top--]); // ? x ?????????????? pushdown (??? Splay ???? 2) 
        //???1:???? isRt ???,????pushdown 
        while (!isRt(x)) { // (??? Splay ???? 3.1) 
            int y = fa[x];
            if (!isRt(y)) { // (??? Splay ???? 3.2) 
                if (rel(x) == rel(y)) rotate(y); else rotate(x);
            }
            rotate(x);
        }
        //pushup(x); ??????  
    }
    void access(int x) { // ????? x ???????????,??? x ???????????????? 
        for (int y = 0; x; y = x, x = fa[x]) 
            splay(x), rc = y, pushup(x);
    }
    void makeRt(int x) { // ?????? 
        access(x), splay(x);
        prorev(x);
    }
    int findRt(int x) { // ????? x ?? 
        access(x), splay(x);
        while (pushdown(x), lc) x = lc;
        splay(x); // splay ???? 
        return x;
    }
    void split(int x, int y) { // ???????? x-y 
        makeRt(x);
        access(y), splay(y); // splay ???? 
        //???2:?(x)?y??????,???? access ??,y ??????,????? 
    }
    void link(int x, int y) { // ?????? x-y 
        makeRt(x);
        if (findRt(y) != x) fa[x] = y;
    }
    void cut(int x, int y) { // ?????? x-y 
        makeRt(x);
        if (findRt(y) == x && fa[y] == x && !ch[y][0]) {
            fa[y] = rc = 0;
            pushup(x);
        }
        //split(x, y), fa[x] = ch[y][0] = 0, pushup(y);
    }

    void clean() {
        ms(val, 0), ms(ch, 0), ms(fa, 0), ms(rev, 0), ms(maxv, 0), ms(id, 0);
    }
    int solve() {

        clean();
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= m; ++i) scanf("%d%d%d%d", &ed[i].x, &ed[i].y, &ed[i].a, &ed[i].b);
        sort(ed + 1, ed + 1 + m);

        for (int i = 1; i <= m; ++i) val[i + n] = ed[i].b;

        int ans = INF;
        for (int i = 1; i <= m; ++i) {
            int &u = ed[i].x, &v = ed[i].y;
            if (u == v) continue ;
            if (findRt(u) == findRt(v)) {
                split(u, v);
                if (val[id[v]] <= ed[i].b) {
                    if (findRt(1) == findRt(n)) {
                        split(1, n);
                        ans = min(ans, ed[i].a + maxv[n]);
                    }
                    continue ;
                }
                int whw = id[v]; // 一定要这样!!id会变化!!
                cut(whw, ed[whw - n].x);
                cut(whw, ed[whw - n].y);
                link(u, i + n);
                link(v, i + n);
            } else {
                link(u, i + n);
                link(v, i + n);
            }

            if (findRt(1) == findRt(n)) {
                split(1, n);
                ans = min(ans, ed[i].a + maxv[n]);
            }
        }

        if (ans == INF) cout << -1; else cout << ans;

        return 0;
    }
}
int main() {
    flyinthesky::solve();
    return 0;
}
/*
4 6
2 4 2 9
4 3 4 6
2 3 6 5
4 4 9 9
4 1 7 7
1 3 7 4

4 4
1 3 7 4
4 3 4 6 
2 3 6 5
2 4 2 9

4 6
2 3 8 10
4 1 9 9
2 1 7 8
3 1 7 7
4 3 10 5
3 1 2 10

4 3
4 1 9 9
3 1 7 7
4 3 10 5

*/
------ 本文结束 ------