[bzoj3669][uoj3][Noi2014]魔法森林【link-cut-tree】

【題目鏈接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=3669
  http://uoj.ac/problem/3
【題解】
  首先我們可以從小到大枚舉a的最大值,然後維護b的最小生成樹。顯然需要用到lct,然而lct並不能維護邊信息,所以我們需要把每條邊變成一個點並向兩頭連邊。每次新加入一條邊(u,v) ,先判斷u,v 是否連通,如果不連通,直接接上。否則先將這條邊與原來路徑上的最大值比較,若更優則替換。
  時間複雜度O((N+M)log(N+M))
【代碼】

/* - - - - - - - - - - - - - - -
    User :      VanishD
    problem :   [noi2014][bzoj3669]
    Points :    LCT
- - - - - - - - - - - - - - - */
# include <bits/stdc++.h>
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    N       50010
# define    M       200010
using namespace std;
int read(){
    int tmp = 0, fh = 1; char ch = getchar();
    while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }
    return tmp * fh;
}
struct Node{
    int u, v, a, b;
}eg[M];
bool cmp(Node x, Node y){
    return x.a < y.a;
}
int n, m, num[M], ans;
struct lct{
    struct Tree{
        int pl, pr, mx, fa, tag, mxi;
    }T[M];
    int st[M], top;
    void pushtag(int p){
        if (T[p].tag != 0){
            swap(T[p].pl, T[p].pr);
            T[T[p].pl].tag ^= 1; T[T[p].pr].tag ^= 1;
        }
        T[p].tag = 0;
    }
    void reget(int p){
        if (p == 0) return;
        if (T[T[p].pl].mx >= T[T[p].pr].mx)
            T[p].mx = T[T[p].pl].mx, T[p].mxi = T[T[p].pl].mxi;
            else T[p].mx = T[T[p].pr].mx, T[p].mxi = T[T[p].pr].mxi;
        if (num[p] > T[p].mx)
            T[p].mx = num[p], T[p].mxi = p; 
    }
    bool isroot(int p){
        return (T[T[p].fa].pl != p && T[T[p].fa].pr != p);
    }
    void zig(int x){
        int y = T[x].fa;
        if (!isroot(y)){
            if (T[T[y].fa].pl == y)
                T[T[y].fa].pl = x; else T[T[y].fa].pr = x;
        }
        T[x].fa = T[y].fa;
        T[y].pl = T[x].pr; T[T[x].pr].fa = y;
        T[y].fa = x; T[x].pr = y;
        reget(y), reget(x);
    }
    void zag(int x){
        int y = T[x].fa;
        if (!isroot(y)){
            if (T[T[y].fa].pl == y)
                T[T[y].fa].pl = x; else T[T[y].fa].pr = x;
        }
        T[x].fa = T[y].fa;
        T[y].pr = T[x].pl; T[T[x].pl].fa = y;
        T[y].fa = x; T[x].pl = y;
        reget(y), reget(x);
    }
    void splay(int p){
        st[top = 1] = p; int x = p;
        while (!isroot(x)) st[++top] = x = T[x].fa;
        for (int i = top; i >= 1; i--) 
            pushtag(st[i]);
        x = p;
        while (!isroot(x)){
            int y = T[x].fa;
            if (isroot(y)){
                if (T[y].pl == x) zig(x);
                    else zag(x);
                break;
            }
            if (T[T[y].fa].pl == y)
                if (T[y].pl == x) zig(y), zig(x);
                    else zag(x), zig(x);
                else if (T[y].pl == x) zig(x), zag(x);
                    else zag(y), zag(x);
        } 
    }
    void access(int x){
        int v = x, u = 0;
        while (v){
            splay(v);
            T[v].pr = u;
            u = v;
            v = T[v].fa; 
        }
        splay(x);
    }
    void toroot(int x){
        access(x);
        T[x].tag ^= 1;
    }
    int findroot(int x){
        pushtag(x);
        while (T[x].pl != 0){
            x = T[x].pl;
            pushtag(x);
        }
        return x;
    }
    void link(int u, int v){
        toroot(u);
        T[u].fa = v;
    }
    void cut(int u, int v){
        toroot(u);
        access(v), splay(v);
        T[v].pl = 0; T[u].fa = 0;
        reget(v);
    }
    bool in(int u, int v){
        toroot(u);
        access(v);
        return findroot(v) == u;
    }
    int findmax(int u, int v){
        toroot(u);
        access(v);
        return T[v].mxi;
    }
}a;
int main(){
//  freopen(".in", "r", stdin);
//  freopen(".out", "w", stdout);
    n = read(), m = read();
    for (int i = 1; i <= m; i++){
        eg[i].u = read(); eg[i].v = read();
        eg[i].a = read(); eg[i].b = read();
    }
    sort(eg + 1, eg + m + 1, cmp);
    for (int i = 1; i <= m; i++)
        num[i + n] = eg[i].b;
    for (int i = 1; i <= n + m; i++){
        a.T[i].mx = num[i];
        a.T[i].mxi = i;
    }
    a.T[0].mx = -1;
    ans = inf;
    for (int i = 1; i <= m; i++){
        if (eg[i].u == eg[i].v) continue;
        if (a.in(eg[i].u, eg[i].v)){
            int tmp = a.findmax(eg[i].u, eg[i].v);
            if (num[tmp] >= num[i + n]){
                a.cut(eg[tmp - n].u, tmp);
                a.cut(eg[tmp - n].v, tmp);
                a.link(eg[i].u, i + n);
                a.link(eg[i].v, i + n);
            }
        }
        else {
            a.link(eg[i].u, i + n);
            a.link(eg[i].v, i + n);
        }
        if (a.in(1, n))
            ans = min(ans, eg[i].a + num[a.findmax(1, n)]);
    }
    printf("%d\n", (ans == inf) ? -1 : ans); 
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章