[BZOJ3669][NOI2014]魔法森林 LCT

把边按权值 a a 从小到大插入,并维护权值b的最小生成树。具体地,如果link的一条边的两个端点已经联通,那么把这两个点路径上 b b 最大的边cut掉。最后用1 n n 路径上b的最小值加上当前 a a <script type="math/tex" id="MathJax-Element-28">a</script>更新答案。
注意到边权不太好维护,可以把每条边重开一个点,向两个端点连边。
代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define N 150010
#define T(x,y) ((x)?(x)->y:0)
using namespace std;
int n,m,w[N],fa[N],ans;
int read()
{
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
struct edge
{
    int x,y,a,b;
}e[N];
bool cmpa(edge p,edge q)
{
    return p.a<q.a;
}
int getfa(int v)
{
    if(fa[v]==v) return v;
    return (fa[v]=getfa(fa[v]));
}
struct tree
{
    tree *s[2],*f;
    int id,mx;
    bool rev;
    tree(int x){s[0]=s[1]=f=0;rev=0;id=x;mx=w[x];}
    bool isroot()
    {
        return (T(f,s[0])!=this&&T(f,s[1])!=this);
    }
    bool son()
    {
        return f->s[1]==this;
    }
    void update()
    {
        mx=id;
        if(w[T(s[0],mx)]>w[mx]) mx=T(s[0],mx);
        if(w[T(s[1],mx)]>w[mx]) mx=T(s[1],mx);
    }
    void reverse()
    {
        rev^=1;swap(s[1],s[0]);
    }
    void pushdown()
    {
        if(rev)
        {
            if(s[0]) s[0]->reverse();
            if(s[1]) s[1]->reverse();
            rev=0;
        }
    }
    void rotate()
    {
        pushdown();
        int b=son();

        f->s[b]=s[b^1];
        if(s[b^1]) s[b^1]->f=f;
        s[b^1]=f;f=f->f;s[b^1]->f=this;

        if(T(f,s[0])==s[b^1]) f->s[0]=this;
        else if(T(f,s[1])==s[b^1]) f->s[1]=this; 
        s[b^1]->update();
        update();   
    }
    void splay()
    {
        while(!isroot())
        {
            if(f->isroot()) f->pushdown(),rotate();
            else
            {
                f->f->pushdown();
                f->pushdown();
                if(son()^f->son()) rotate();
                else f->rotate();
                rotate();
            }
        }
    }
    void access()
    {
        for(tree *x=this,*y=0;x;y=x,x=x->f)
            x->splay(),x->pushdown(),x->s[1]=y,x->update();
    }
    void makeroot()
    {
        access();
        splay();
        reverse();
    }
    void split(tree *y)
    {
        y->makeroot();
        access();
        splay();
    }
    void link(tree *y)
    {
        makeroot();
        f=y;
    }
    void cut(tree *y)
    {
        y->makeroot();
        access();
        splay();
        y->f=s[0]=0;
    }
}*a[N];
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++)
        e[i].x=read(),e[i].y=read(),e[i].a=read(),e[i].b=read();    
    sort(e+1,e+m+1,cmpa);
    for(int i=1;i<=n;i++)
        a[i]=new tree(i);
    for(int i=n+1;i<=n+m;i++)
        w[i]=e[i-n].b,a[i]=new tree(i);
    for(int i=1;i<=n;i++)
        fa[i]=i; 
    ans=1e9;
    for(int i=1;i<=m;i++)
    {
        int x=e[i].x,y=e[i].y;
        if(x==y) continue;
        if(getfa(x)==getfa(y))
        {   
            a[x]->split(a[y]);
            int t=a[x]->mx;
            if(e[i].b>=w[t]) continue;
            a[t]->cut(a[e[t-n].x]);
            a[t]->cut(a[e[t-n].y]);
        }
        a[i+n]->link(a[e[i].x]);
        a[i+n]->link(a[e[i].y]);
        fa[fa[x]]=fa[y];
        if(getfa(1)==getfa(n)) 
            a[1]->split(a[n]),ans=min(ans,w[a[1]->mx]+e[i].a);
    }
    printf("%d",ans==1e9?-1:ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值