BZOJ- 3669 魔法森林

LCT打卡了,啦啦啦


​​​​​​​

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAXN=55555;
const int MAXM=111111;
struct edge_t
{
    int u,v,w1,w2;
    bool operator < (const edge_t& r)const
    {
        return w1<r.w1;
    }
}edge[MAXM];
//现在有点逗比的就是这个现在我们要把每个边权对应到一个点权
//那我们怎么做呢,首先的想法是把一条边拆成一个点和两条边~~~
const int SIZE=MAXN+MAXM;
int ch[SIZE][2],pre[SIZE],key[SIZE];
int rev[SIZE],Max[SIZE];
bool rt[SIZE];
void Update_Rev(int r)
{
    if(!r) return;
    swap(ch[r][0],ch[r][1]);
    rev[r]^=1;
}
void push_down(int r)
{
    if(rev[r])
    {
        Update_Rev(ch[r][0]);
        Update_Rev(ch[r][1]);
        rev[r]=0;
    }
}
void push_up(int r)
{
    Max[r]=max(key[r],max(Max[ch[r][0]],Max[ch[r][1]]));
}
void Rotate(int x)
{
    int y=pre[x],kind=ch[y][1]==x;
    ch[y][kind]=ch[x][!kind];
    pre[ch[y][kind]]=y;
    pre[x]=pre[y];
    pre[y]=x;
    ch[x][!kind]=y;
    if(rt[y]) rt[y]=false,rt[x]=true;
    else ch[pre[x]][ch[pre[x]][1]==y]=x;
    push_up(y);
}
void P(int r)
{
    if(!rt[r]) P(pre[r]);
    push_down(r);
}
void Splay(int r)
{
    P(r);
    while(!rt[r])
    {
        int f=pre[r],ff=pre[f];
        if(rt[f]) Rotate(r);
        else if((ch[ff][1]==f)==(ch[f][1]==r)) Rotate(f),Rotate(r);
        else Rotate(r),Rotate(r);
    }
    push_up(r);
}
int Access(int x)
{
    int y=0;
    for(;x;x=pre[y=x])
    {
        Splay(x);
        rt[ch[x][1]]=true,rt[ch[x][1]=y]=false;
        push_up(x);
    }
    return y;
}
bool Judge(int u,int v)
{
    while(pre[u]) u=pre[u];
    while(pre[v]) v=pre[v];
    return u==v;
}
void mroot(int r)
{
    Access(r);
    Splay(r);
    Update_Rev(r);
}
void link(int u,int v)
{
    mroot(u);
    pre[u]=v;
}
void cut(int u,int v)
{
    mroot(u);
    Splay(v);
    pre[ch[v][0]]=pre[v];
    pre[v]=0;
    rt[ch[v][0]]=true;
    ch[v][0]=0;
    push_up(v);
}
void lca(int &u,int &v)
{
    Access(v),v=0;
    while(u)
    {
        Splay(u);
        if(!pre[u]) return;
        rt[ch[u][1]]=true;
        rt[ch[u][1]=v]=false;
        push_up(u);
        u=pre[v=u];
    }
}
int query(int u,int v)
{
    if(!Judge(u,v)) return -1;
    lca(u,v);
    return max(key[u],max(Max[v],Max[ch[u][1]]));
}
//找到这一棵伸展树上最大的节点的编号
int find(int r)
{
    int Maxs=Max[r];
    if(key[r]==Maxs) return r;
    if(Max[ch[r][0]]==Maxs) return find(ch[r][0]);
    else return find(ch[r][1]);
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=m;i++)
            scanf("%d%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w1,&edge[i].w2);
        sort(edge+1,edge+m+1);
        for(int i=1;i<=n+m;i++)
        {
            if(i>n) key[i]=Max[i]=edge[i-n].w2;
            else key[i]=Max[i]=0;
            pre[i]=ch[i][0]=ch[i][1]=0;
            rev[i]=0;
            rt[i]=true;
        }
        //然后我这里用LCT维护最小生成树
        //link-cut tree查询这一段路径上面的最小值
        int ans=-1;
        for(int i=1;i<=m;i++)
        {
            int u=edge[i].u;
            int v=edge[i].v;
            int w=edge[i].w2;
            int Edge=i+n;
            //首先我们判断一下u,v的连通性,如果u,v两个节点是联通的,那么我们把这条边加上去明显会形成环对吧
            //如果当前加入的节点距离比这条路径上面的最大值的那一条边小的话
            //那么我们用这条边代替那条边就行
            int Maxs=query(u,v);
            if(Maxs==-1)
            {
                link(u,Edge);
                link(Edge,v);
            }
            else if(Maxs>w)
            {
                //我们现在要找到一条最大的边,并且取缔这条边
                int rt;
                int tmpu=u,tmpv=v;
                lca(tmpu,tmpv);
                if(Maxs==Max[tmpv]) rt=find(tmpv);
                else rt=find(ch[tmpu][1]);
                int cutu=edge[rt-n].u;
                int cutv=edge[rt-n].v;
                cut(cutu,rt);
                cut(rt,cutv);
                link(u,Edge);
                link(Edge,v);
            }
            int tmpans=query(1,n);
            if(tmpans!=-1)
            {
                if(ans==-1) ans=tmpans+edge[i].w1;
                else ans=min(ans,tmpans+edge[i].w1);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值