P2387 [NOI2014]魔法森林 LCT维护最小生成树

\(\color{#0066ff}{ 题目描述 }\)

为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士。魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…,m。初始时小 E 同学在 1 号节点,隐士则住在 n 号节点。小 E 需要通过这一片魔法森林,才能够拜访到隐士。

魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪 就会对其发起攻击。幸运的是,在 1 号节点住着两种守护精灵:A 型守护精灵与 B 型守护精灵。小 E 可以借助它们的力量,达到自己的目的。

只要小 E 带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无 向图中的每一条边 ei 包含两个权值 ai 与 bi 。若身上携带的 A 型守护精灵个数不 少于 ai ,且 B 型守护精灵个数不少于 bi ,这条边上的妖怪就不会对通过这条边 的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向 小 E 发起攻击,他才能成功找到隐士。

由于携带守护精灵是一件非常麻烦的事,小 E 想要知道,要能够成功拜访到 隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为 A 型守护精灵的 个数与 B 型守护精灵的个数之和。

\(\color{#0066ff}{输入格式}\)

输入文件的第 1 行包含两个整数 n,m,表示无向图共有 n 个节点,m 条边。 接下来 m 行,第i+ 1 行包含 4 个正整数 Xi,Yi,ai,bi,描述第i条无向边。 其中Xi与 Yi为该边两个端点的标号,ai 与 bi 的含义如题所述。 注意数据中可能包含重边与自环。

\(\color{#0066ff}{输出格式}\)

输出一行一个整数:如果小 E 可以成功拜访到隐士,输出小 E 最少需要携 带的守护精灵的总个数;如果无论如何小 E 都无法拜访到隐士,输出“-1”(不 含引号)。

\(\color{#0066ff}{输入样例}\)

4 5 
1 2 19 1 
2 3 8 12 
2 4 12 15 
1 3 17 8 
3 4 1 17 
    
    
    
3 1 
1 2 1 1 

\(\color{#0066ff}{输出样例}\)

32

    
-1

\(\color{#0066ff}{数据范围与提示}\)

* 解释1

如果小 E 走路径 1→2→4,需要携带 19+15=34 个守护精灵; 如果小 E 走路径 1→3→4,需要携带 17+17=34 个守护精灵; 如果小 E 走路径 1→2→3→4,需要携带 19+17=36 个守护精灵; 如果小 E 走路径 1→3→2→4,需要携带 17+15=32 个守护精灵。 综上所述,小 E 最少需要携带 32 个守护精灵。

* 解释2

小 E 无法从 1 号节点到达 3 号节点,故输出-1。

img

\(\color{#0066ff}{ 题解 }\)

把所有边按a排序,用LCT维护边权为b的生成树(注意新加点使边权变成点权)

每次向内加边,a已知(已排序),把链上最大的b换下来,每次取min即可

#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 5e4 + 100;
const int inf = 0x7fffffff;
struct node {
    int x, y, a, b;
    friend bool operator < (const node &a, const node &b) { return a.a < b.a; }
}e[maxn << 1];
struct LCT {
protected:
    struct node {
        node *fa, *ch[2];
        int max, val, rev;
        node(int max = 0, int val = 0, int rev = 0): max(max), val(val), rev(rev) {}
        void trn() { std::swap(ch[0], ch[1]); rev ^= 1; }
        void upd() {
            max = val;
            if(ch[0]) max = std::max(max, ch[0]->max);
            if(ch[1]) max = std::max(max, ch[1]->max);
        }
        void dwn() {
            if(!rev) return;
            if(ch[0]) ch[0]->trn();
            if(ch[1]) ch[1]->trn();
            rev = 0;
        }
        bool ntr() { return fa && (fa->ch[1] == this || fa->ch[0] == this); }
        bool isr() { return this == fa->ch[1]; }
        void clr() {
            if(ch[0]) ch[0]->fa = NULL;
            if(ch[1]) ch[1]->fa = NULL;
            ch[0] = ch[1] = NULL;
        }
    }pool[maxn << 2];
    void rot(node *x) {
        node *y = x->fa, *z = y->fa;
        bool k = x->isr(); node *w = x->ch[!k];
        if(y->ntr()) z->ch[y->isr()] = x;
        x->ch[!k] = y, y->ch[k] = w;
        y->fa = x, x->fa = z;
        if(w) w->fa = y;
        y->upd(), x->upd();
    }
    void splay(node *o) {
        static node *st[maxn << 2];
        int top;
        st[top = 1] = o;
        while(st[top]->ntr()) st[top + 1] = st[top]->fa, top++;
        while(top) st[top--]->dwn();
        while(o->ntr()) {
            if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
            rot(o);
        }
    }
    void access(node *x) {
        for(node *y = NULL; x; x = (y = x)->fa)
            splay(x), x->ch[1] = y, x->upd();
    }
    void makeroot(node *x) { access(x), splay(x), x->trn(); }
    node *findroot(node *x) {
        access(x), splay(x);
        while(x->dwn(), x->ch[0]) x = x->ch[0];
        return x;
    }
    void link(node *x, node *y) { makeroot(x), x->fa = y; }
    node *findpos(node *x, int max) {
        while(x->dwn(), x->val != max) {
            if(x->ch[0] && x->ch[0]->max == max) x = x->ch[0];
            else x = x->ch[1];
        }
        return x;
    }
public:
    void init(::node *a,int n, int m) { 
        for(int i = n + 1; i <= n + m; i++) 
            pool[i].val = a[i - n].b, pool[i].upd();
    }
    void add(const ::node &a, int id) {
        node *x = pool + a.x, *y = pool + a.y;
        node *o = pool + id;
        if(findroot(x) == findroot(y)) {
            makeroot(x), access(y), splay(y);
            if(a.b >= y->max) return;
            node *d = findpos(y, y->max);
            splay(d);
            d->clr(), d->upd();
        }
        link(x, o), link(o, y);
    }
    int query(int l, int r) {
        node *x = pool + l, *y = pool + r;
        if(findroot(x) != findroot(y)) return -1;
        makeroot(x), access(y), splay(y);
        return y->max;
    }
}s;
int n, m, ans = inf;
int main() {
    n = in(), m = in();
    for(int i = 1; i <= m; i++) 
        e[i].x = in(), e[i].y = in(), e[i].a = in(), e[i].b = in();
    std::sort(e + 1, e + m + 1);
    s.init(e, n, m);
    for(int i = 1; i <= m; i++) {
        s.add(e[i], i + n);
        int v = s.query(1, n);
        if(v == -1) continue;
        ans = std::min(ans, v + e[i].a);
    }
    printf("%d", ans == inf? -1 : ans);
    return 0;
}
posted @ 2019-02-16 16:57  olinr  阅读(140)  评论(0编辑  收藏  举报