bzoj 3669 [Noi2014]魔法森林 LCT

题面

题目传送门

解法

维护双关键字好像不太可做吧

所以我们先按照\(a\)从小到大排序,然后逐一检查\(b\)

如果构成环,那么把环上最大的\(b\)删掉

只要出现1和\(n\)连通的时候就更新答案

时间复杂度:\(O((n+m)\ log\ n)\)

代码

#include <bits/stdc++.h>
#define N 150010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Edge {
    int x, y, p, q;
    bool operator < (const Edge &a) const {
        return p < a.p;
    }
} a[N];
namespace LCT {
    struct Node {
        int fa, num, rev, val, pathfa, child[2];
    } t[N];
    int son(int x, int y) {return t[x].child[1] == y;}
    int val(int x) {if (!x) return 0; return t[x].val;}
    int num(int x) {if (!x) return 0; return t[x].num;}
    void rev(int x) {if (!x) return; t[x].rev ^= 1;}
    void update(int x) {
        if (!x) return; t[x].num = x;
        if (val(t[x].num) < val(num(t[x].child[0]))) t[x].num = num(t[x].child[0]);
        if (val(t[x].num) < val(num(t[x].child[1]))) t[x].num = num(t[x].child[1]);
    }
    void pushdown(int x) {
        if (!x) return;
        int k = t[x].rev;
        if (k) {
            rev(x), swap(t[x].child[0], t[x].child[1]);
            rev(t[x].child[0]), rev(t[x].child[1]);
        }
    }
    void Connect(int x, int y, int k) {
        if (x) t[x].child[k] = y;
        if (y) t[y].fa = x; update(x);
    }
    void Rotate(int x) {
        int y = t[x].fa, z = t[y].fa;
        pushdown(y), pushdown(x);
        swap(t[x].pathfa, t[y].pathfa);
        int a = son(y, x), b = !a;
        Connect(z, x, son(z, y));
        Connect(y, t[x].child[b], a);
        Connect(x, y, b);
        update(y), update(x);
    }
    void Splay(int x) {
        while (t[x].fa) {
            int y = t[x].fa, z = t[y].fa;
            if (z) {
                pushdown(z), pushdown(y);
                (son(z, y) ^ son(y, x)) ? Rotate(x) : Rotate(y);
            }
            Rotate(x);
        }
    }
    void expose(int x) {
        Splay(x), pushdown(x);
        int y = t[x].child[1];
        t[y].fa = 0, t[y].pathfa = x;
        t[x].child[1] = 0, update(x);
    }
    void access(int x) {
        for (expose(x); t[x].pathfa; Splay(x)) {
            expose(t[x].pathfa);
            Connect(t[x].pathfa, x, 1);
            t[x].pathfa = 0;
        }
    }
    void evert(int x) {access(x), Splay(x), rev(x);}
    void link(int x, int y) {evert(y), t[y].pathfa = x;}
    void cut(int x, int y) {
        evert(x), access(y);
        Splay(y), pushdown(y);
        t[t[y].child[0]].fa = 0;
        t[y].child[0] = 0, update(y);
    }
    int query(int x, int y) {
        evert(x); access(y); Splay(y);
        return t[y].num;
    }   
}
int p[N];
int Find(int x) {
    if (p[x] == x) return x;
    return p[x] = Find(p[x]);
}
int main() {
    using namespace LCT;
    int n, m; read(n), read(m);
    for (int i = 1; i <= n; i++) t[i].num = i;
    for (int i = 1; i <= m; i++)
        read(a[i].x), read(a[i].y), read(a[i].p), read(a[i].q);
    sort(a + 1, a + m + 1); int ans = INT_MAX;
    for (int i = n + 1; i <= n + m; i++) t[i].val = a[i - n].q;
    for (int i = 1; i <= n + m; i++) t[i].num = i;
    for (int i = 1; i <= n; i++) p[i] = i;
    for (int i = 1; i <= m; i++) {
        int tx = Find(a[i].x), ty = Find(a[i].y);
        if (tx != ty) {
            p[tx] = ty;
            link(a[i].x, i + n), link(a[i].y, i + n);
        } else {
            int tmp = query(a[i].x, a[i].y);
            if (val(tmp) > a[i].q) {
                int pos = tmp - n, x = a[pos].x, y = a[pos].y;
                cut(x, tmp), cut(y, tmp);
                link(a[i].x, i + n), link(a[i].y, i + n);
            }
        }
        if (Find(1) == Find(n)) {
            int tmp = query(1, n);
            chkmin(ans, a[i].p + val(tmp));
        }
    }
    if (ans != INT_MAX) cout << ans << "\n";
        else cout << "-1\n";
    return 0;
}

转载于:https://www.cnblogs.com/copperoxide/p/9478313.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值