Luogu P2387] [NOI2014]魔法森林

洛谷传送门

题目描述

为了得到书法大家的真传,小 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 型守护精灵的个数之和。

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1:

4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
输出样例#1:
32
输入样例#2:
3 1
1 2 1 1
输出样例#2:
-1

说明
  • 解释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。

解题分析

听说此题 SPFA S P F A 可以过?蒟蒻表示不会这种骚操作…只好老老实实打LCT。

我们可以将所有边按A值排序, 然后一条一条像Kruskal一样连接, 并用并查集判联通。 如果已联通, 就找出原边上的最大B值的点进行比较,若比其小则将它替换掉更优。(因为已经保证A尽量小了)

值得注意的是, 这里LCT维护的是边权, 所以将边作为实点, 原来的节点作为虚节点, 与两端虚节点相连即可达到联通的目的。

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#define R register
#define gc getchar()
#define IN inline
#define W while
#define MX 100005
#define ls tree[now].son[0]
#define rs tree[now].son[1]
#define dad tree[now].fat
template <class T>
IN void in (T &x)
{
    x = 0; R char c = gc;
    W (!isdigit(c)) c = gc;
    W (isdigit(c))
    {x = (x << 1) + (x << 3) + c - 48, c = gc;}
}
namespace LCT
{
    struct Node
    {
        int son[2], fat, mx, pos, val;//pos维护子树中最大b值的位置
        bool rev;
    }tree[(MX << 1) + 10];
    int st[MX], top, line, num, f[MX], dot;
    struct Edge
    {
        int from, to, val_a, val_b;
    }edge[MX];
    IN bool operator < (const Edge &x, const Edge &y)
    {return x.val_a < y.val_a;}
    IN bool nroot(const int &now)
    {return tree[dad].son[1] == now || tree[dad].son[0] == now;}
    IN bool get(const int &now)
    {return tree[dad].son[1] == now;}
    IN void pushrev(const int &now)
    {
        std::swap(ls, rs);
        tree[now].rev ^= 1;
    }
    IN void pushup(const int &now)
    {
        tree[now].mx = tree[now].val, tree[now].pos = now;
        if(ls) if(tree[ls].mx > tree[now].mx) tree[now].mx = tree[ls].mx, tree[now].pos = tree[ls].pos;
        if(rs) if(tree[rs].mx > tree[now].mx) tree[now].mx = tree[rs].mx, tree[now].pos = tree[rs].pos;
    }
    IN void pushdown(const int &now)
    {
        if(tree[now].rev)
        {
            if(ls) pushrev(ls);
            if(rs) pushrev(rs);
        }
        tree[now].rev = false;
    }
    IN void rotate(const int &now)
    {
        R bool dir = get(now);
        R int fa = dad, grand = tree[fa].fat;
        tree[fa].son[dir] = tree[now].son[dir ^ 1];
        tree[tree[now].son[dir ^ 1]].fat = fa;
        if(nroot(fa)) tree[grand].son[get(fa)] = now;
        tree[now].fat = grand;
        tree[fa].fat = now;
        tree[now].son[dir ^ 1] = fa;
        pushup(fa);
    }
    IN void splay(int now)
    {
        R int fa, grand, x = now; top = 0;
        st[++top] = x;
        W (nroot(x)) x = tree[x].fat, st[++top] = x;
        W (top) pushdown(st[top--]);
        W (nroot(now))
        {
            fa = dad, grand = tree[fa].fat;
            if(nroot(fa)) rotate(get(now) == get(fa) ? fa : now);
            rotate(now);
        }
        pushup(now);
    }
    IN void access(int now)
    {
        for (R int x = 0; now; x = now, now = dad)
        splay(now), tree[now].son[1] = x, pushup(now);
    }
    IN void make_root(int now)
    {access(now), splay(now), pushrev(now);}
    IN int find_root(int now)
    {
        access(now), splay(now);
        W (ls) pushdown(now), now = ls;
        return now;
    }
    IN void link(const int &x, const int &y)
    {
        make_root(x);
        if(find_root(y) != x)
            tree[x].fat = y;
    }
    IN void cut(const int &x, const int &y)
    {
        make_root(x);
        if(find_root(y) == x && tree[x].fat == y && !tree[x].son[1])
        tree[x].fat = tree[y].son[0] = 0, pushup(y);
    }
    int find(const int &x)
    {return f[x] == x ? x : f[x] = find(f[x]);}
    IN void split(const int &x, const int &y)
    {make_root(x), access(y), splay(y);}
}
using namespace LCT;
int main(void)
{
    int ans = 999999, v, t1, t2, f1, f2;
    in(dot), in(line);
    for (R int i = 1; i <= line; ++i)
    in(edge[i].from), in(edge[i].to), in(edge[i].val_a), in(edge[i].val_b);
    for (R int i = 1; i <= 100000; ++i) f[i] = i;
    std::sort(edge + 1, edge + 1 + line);
    for (R int i = 1; i <= line; ++i)
    {
        tree[i].val = edge[i].val_b;
        t1 = find(edge[i].from), t2 = find(edge[i].to);
        if(t1 != t2)
        {
            link(edge[i].from + MX, i);
            link(edge[i].to + MX, i);//加上MX的点为原树中的点, 点权为0
            //不加MX的点为实际的排好序的边
            f[t1] = t2;
            if(find(1) == find(dot))
            {
                split(1 + MX, dot + MX);
                if(ans > tree[dot + MX].mx + edge[i].val_a) 
                ans = tree[dot + MX].mx + edge[i].val_a;
            }
        }
        else
        {
            split(edge[i].to + MX, edge[i].from + MX);
            if(tree[edge[i].from + MX].mx > edge[i].val_b)
            {
                v = tree[edge[i].from + MX].pos;
                f1 = edge[v].from;
                f2 = edge[v].to;
                cut(f1 + MX, v);
                cut(f2 + MX, v);
                link(edge[i].to + MX, i);
                link(edge[i].from + MX, i);
                if(find(1) == find(dot))
                {
                    split(1 + MX, dot + MX);
                    if(ans > edge[i].val_a + tree[dot + MX].mx) 
                    ans = edge[i].val_a + tree[dot + MX].mx;
                }
            }
        }
    }
    if(ans == 999999) printf("-1");
    else printf("%d", ans);
}

你可能感兴趣的:(LCT)