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

2 篇文章 0 订阅

P2387 [NOI2014]魔法森林
题意:一个无向图,每个点都有两个权值a和b,求一条1-n路径上,a和b的路径最大值的和最小
此题因为有两个关键字,如果只有一个关键字,那就是最小生成树,对于两个关键字,可以先对a从小到大排序,然后把边看做一个点,在lct上把这条边对应的点和其在原图上所连的两个点link起来,前提是他们不能再一个树中,如果在一棵树中,肯定会成环,这是不允许的,所以要截断这个环上的一条边,比较这个新加入的边的b值是否比这个环上b的最大值还大,如果还大就不加入,否则把最大的b值删去,再加入新节点,注意b值只对应原图为边的lct节点,原图为点的lct节点上不对应b值(就是0),对于这个环上最大值,由于我们还没有把新的边加入,所以直接查询新加边的两个节点的链上的mx节点,可以lct维护。
注意分清楚原图上的点和lct上的点,主要主函数就可以理解了

#include <bits/stdc++.h>
#define jh(x, y) x ^= y, y ^= x, x ^= y
#define lc ch[x][0]
#define rc ch[x][1]
using namespace std;
inline void read(int &x){
    x = 0; int f = 1;  char ch = getchar();
    while (!(ch >= '0' && ch <= '9')){if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
inline void Max(int &x, int y){if (y > x) x = y;}
inline void Min(int &x, int y){if (y < x) x = y;}
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
struct Node{int u, v, a, b;} e[N];
inline bool cmp(Node x, Node y){return x.a < y.a;}
int n, m, ans = INF;
int val[N], ch[N][2], prt[N], mx[N], r[N], fa[N];
inline int getfa(int x){
    if (prt[x] == x) return x;
    return prt[x] = getfa(prt[x]);
}
inline int chk(int x){return x == ch[fa[x]][1];}
inline int nroot(int x){return x == ch[fa[x]][chk(x)];}
inline void pushup(int x){
    mx[x] = x;
    if (val[mx[lc]] > val[mx[x]]) mx[x] = mx[lc];
    if (val[mx[rc]] > val[mx[x]]) mx[x] = mx[rc];
}
inline void pushr(int x){jh(lc, rc); r[x] ^= 1;}
inline void pushdown(int x){
    if (r[x]){
        r[x] = 0;
        if (lc) pushr(lc);
        if (rc) pushr(rc);
    }
}
inline void Rotate(int x){
    int y = fa[x], z = fa[y], k = chk(x), w = ch[x][k ^ 1];
    if (nroot(y)) ch[z][chk(y)] = x;
    ch[y][k] = w, ch[x][k ^ 1] = y;
    if (w) fa[w] = y;
    fa[y] = x, fa[x] = z;
    pushup(y);
}
inline void pushall(int x){if (nroot(x)) pushall(fa[x]); pushdown(x);}
inline void splay(int x){
    pushall(x);
    while (nroot(x)){
        int y = fa[x];
        if (nroot(y)) Rotate((chk(x) ^ chk(y)) ? x : y);
        Rotate(x);
    }
    pushup(x);
}
inline void access(int x){
    for (int y = 0; x; x = fa[y = x])
        splay(x), rc = y, pushup(x);
}
inline void makert(int x){access(x), splay(x), pushr(x);}
inline int findrt(int x){
    access(x), splay(x);
    while (lc) pushdown(x), x = lc;
    splay(x); return x;
}
inline void link(int x, int y){makert(x); if (findrt(y) != x) fa[x] = y;}
inline void cut(int x, int y){
    makert(x);
    if (findrt(y) == x && fa[y] == x && !ch[y][0])
        fa[y] = rc = 0, pushup(x);
}
inline int query(int x, int y){makert(x), access(y), splay(y); return mx[y];}
int main(){
    read(n), read(m);
    for (int i = 1; i <= n; i++) prt[i] = i;
    for (int i = 1; i <= m; i++) read(e[i].u), read(e[i].v), read(e[i].a), read(e[i].b);
    sort(e + 1, e + 1 + m, cmp);
    for (int i = 1; i <= m; i++){
        int u = e[i].u, v = e[i].v;
        if (getfa(u) == getfa(v)){
            int t = query(u, v);
            if (val[t] > e[i].b) cut(t, e[t - n].u), cut(t, e[t - n].v);
            else{
                if (getfa(1) == getfa(n)) ans = min(ans, e[i].a + val[query(1, n)]);
                continue;
            }
        }else prt[getfa(u)] = getfa(v);
        val[n + i] = e[i].b; mx[n + i] = n + i;
        link(u, n + i), link(v, n + i);
        if (getfa(1) == getfa(n)) ans = min(ans, e[i].a + val[query(1, n)]);
    }
    if (ans == INF) puts("-1");
    else printf("%d\n", ans);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值