BZOJ3669 [Noi2014]魔法森林

[Solution]

I thought of Dijkstra at the first time, then found it wrong. Consider add edges of va-increasing order. We need to modify the minimal spanning tree of vb. That's a classic problem of LCT. I just spent 30 minutes writing it in the exams.


[Code]

#define PROC "forest"
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <memory.h>
#include <algorithm>
 
using namespace std;
 
struct edge {
    int a, b, va, vb;
};
 
inline bool cmpEdge(const edge& a, const edge& b) {
    return a. vb < b. vb;
}
 
int nextInt() {
    int d, s = 0;
    do
        d = getchar();
    while (!isdigit(d));
    do
        s = s * 10 + d - 48, d = getchar();
    while (isdigit(d));
    return s;
}
 
const int maxn = 100009;
const int maxe = 100009;
const int inf = 0x3f3f3f3f;
 
int n, m, ans;
int ls[maxn], rs[maxn], pt[maxn], sz[maxn], vl[maxn], vt[maxn], ns[maxn], tns, ea[maxn], eb[maxn], rv[maxn];
edge e[maxe];
 
int newNode(int v0 = 0) {
    int p = ns[-- tns];
    ls[p] = 0;
    rs[p] = 0;
    pt[p] = 0;
    sz[p] = 1;
    rv[p] = 0;
    vl[p] = v0;
    vt[p] = v0;
    return p;
}
 
inline bool isRoot(int p) {
    return !pt[p] || (p != ls[pt[p]] && p != rs[pt[p]]);
}
 
#define getVal(x) ((x)?vt[x]:0)
 
inline void update(int p) {
    sz[p] = sz[ls[p]] + sz[rs[p]] + 1;
    vt[p] = max(vl[p], max(getVal(ls[p]), getVal(rs[p])));
}
 
inline void fix(int p) {
    if (rv[p]) {
        rv[ls[p]] ^= 1;
        rv[rs[p]] ^= 1;
        rv[p] = 0;
        swap(ls[p], rs[p]);
    }
}
 
void lRot(int p) {
    int f = pt[p];
    int a = pt[f];
    if (a) {
        if (f == ls[a])
            ls[a] = p;
        else if (f == rs[a])
            rs[a] = p;
    }
    pt[p] = a;
    pt[f] = p;
    ls[f] = rs[p];
    if (rs[p])
        pt[rs[p]] = f;
    rs[p] = f;
    update(f);
    update(p);
}
 
void rRot(int p) {
    int f = pt[p];
    int a = pt[f];
    if (a) {
        if (f == ls[a])
            ls[a] = p;
        else if (f == rs[a])
            rs[a] = p;
    }
    pt[p] = a;
    pt[f] = p;
    rs[f] = ls[p];
    if (ls[p])
        pt[ls[p]] = f;
    ls[p] = f;
    update(f);
    update(p);
}
 
void splay(int p) {
    static int rp[maxn];
    int tp = 0;
    rp[tp ++] = p;
    for (int q = p; !isRoot(q); q = pt[q])
        rp[tp ++] = pt[q];
    for (int i = tp - 1; i >= 0; i --)
        fix(rp[i]);
    while (!isRoot(p)) {
        int f = pt[p];
        if (isRoot(f)) {
            if (p == ls[f])
                lRot(p);
            else
                rRot(p);
        }
        else {
            int a = pt[f];
            if (f == ls[a]) {
                if (p == ls[f])
                    lRot(f), lRot(p);
                else
                    rRot(p), lRot(p);
            }
            else {
                if (p == rs[f])
                    rRot(f), rRot(p);
                else
                    lRot(p), rRot(p);
            }
        }
    }
}
 
int expose(int p) {
    int q = 0;
    for (; p; p = pt[p]) {
        splay(p);
        rs[p] = q;
        update(p);
        q = p;
    }
    while (1) {
        fix(q);
        if (ls[q])
            q = ls[q];
        else
            break;
    }
    return q;
}
 
void makeRoot(int p) {
    expose(p);
    splay(p);
    rv[p] ^= 1;
}
 
int maxEdge(int p, int q) {
    int p0;
    makeRoot(p);
    expose(q);
    splay(q);
    p0 = q;
    while (1)
        if (vt[p0] == vl[p0])
            break;
        else if (ls[p0] && vt[p0] == vt[ls[p0]])
            p0 = ls[p0];
        else
            p0 = rs[p0];
    return p0;
}
 
void cut(int p) {
    makeRoot(ea[p]);
    expose(eb[p]);
    splay(p);
    pt[ls[p]] = pt[p];
    pt[rs[p]] = pt[p];
    ns[tns ++] = p;
}
 
void tryAdd(edge x) {
    int p = x. a, q = x. b;
    if (expose(p) != expose(q)) {
        int pe = newNode(x. va);
        makeRoot(p);
        rs[pe] = p;
        pt[p] = pe;
        pt[pe] = q;
        ea[pe] = x. a;
        eb[pe] = x. b;
        update(pe);
    }
    else {
        int c = maxEdge(p, q);
        if (vl[c] > x. va) {
            cut(c);
            int pe = newNode(x. va);
            makeRoot(p);
            rs[pe] = p;
            pt[p] = pe;
            pt[pe] = q;
            ea[pe] = x. a;
            eb[pe] = x. b;
            update(pe);
        }
    }
}
 
int getAns() {
    if (expose(1) != expose(n))
        return -1;
    else {
        makeRoot(1);
        expose(n);
        splay(n);
        return vt[n];
    }
}
 
int main() {
    tns = 0;
    n = nextInt();
    m = nextInt();
    for (int i = 0; i < m; i ++) {
        e[i]. a = nextInt();
        e[i]. b = nextInt();
        e[i]. va = nextInt();
        e[i]. vb = nextInt();
    }
    sort(e, e + m, cmpEdge);
    for (int i = 1; i <= n; i ++) {
        ls[i] = 0;
        rs[i] = 0;
        pt[i] = 0;
        sz[i] = 1;
        vl[i] = 0;
        vt[i] = 0;
        rv[i] = 0;
    }
    for (int i = n + 1; i < maxn; i ++)
        ns[tns ++] = i;
    ans = -1;
    for (int i = 0; i < m; i ++) 
        if (e[i]. a != e[i]. b) {
            tryAdd(e[i]);
            int g = getAns();
            if (g > -1 && (ans == -1 || e[i]. vb + g < ans))
                ans = e[i]. vb + g;
        }
    printf("%d\n", ans);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值