zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    从0开始的Python学习013编写一个Python脚本
    Spring Cloud Eureka基本概述
    RocketMQ入门案例
    Quartz使用记录总结
    Spring Boot 1.5升级2.1 主要问题汇总
    Eureka客户端注册多网卡下IP选择问题
    Spring Security之动态配置资源权限
    代码规范问题
    Spring Security + OAuth系统环境搭建(一)
    Java并发编程之AQS
  • 原文地址:https://www.cnblogs.com/olinr/p/10388322.html
Copyright © 2011-2022 走看看