Luogu P4234 最小差值生成树

题意

给定一个 (n) 个点 (m) 条边的有权无向图,求出原图的一棵生成树使得该树上最大边权与最小边权的差值最小。

( exttt{Data Range:}1leq nleq 5 imes 10^4,1leq mleq 2 imes 10^5,1leq wleq 5 imes 10^4)

题解

这位傻逼把 (n) 个节点的树所含有的边的数目认为是 (n),还在出现环的时候立即统计答案(可能生成树都没构建出来),导致 ( exttt{WA}) 了一发又一发,请大家不要学他。

直接进入正题。

考虑先将边权从小到大排序,然后一条一条的考虑。

如果这条边加入当前的图中不会成环的话,那么直接加进来即可。

如果成了环的话,考虑在环上删去边权最小的边。

为什么这么做是正确的呢?

假设我们现在考虑的是第 (k) 条边,连接 (u_i)(v_i),边权为 (w_i),讨论一下当前图中边权最小的边的位置。

  • 当这条边的位置在环上的时候,由于新图的边权最小值改变了,所以需要更新答案。

  • 当这条边的位置不在环上的时候,对答案无影响。

所以我们每一次需要更新答案的时候我们都进行了更新,自然答案就是对的了。

加入完这条边之后,如果当前的图是原图的一棵生成树的话那么更新一下答案就好了。

注意到整个过程涉及到删边和连边操作,所以采用 ( exttt{LCT}) 来维护生成树。

更新答案时,由于要找到最小边权的边的位置,并且每一次加完边之后最小边权的边的位置单调不降(因为排过序了),打标记+移动指针就可以了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=3e5+51;
struct Edge{
    ll from,to,dist;
    inline bool operator <(const Edge &rhs)const
    {
        return dist<rhs.dist;
    }
};
ll n,m,ptr=1,cur,x,y,offset=60000,res;
Edge ed[MAXN];
ll ffa[MAXN],vis[MAXN];
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
inline ll find(ll x)
{
    return x==ffa[x]?x:ffa[x]=find(ffa[x]);
}
namespace LCT{
    struct Node{
        ll fa,mn,val,rv,sz;
        ll ch[2];
    };
    struct LinkCutTree{
        Node nd[MAXN];
        ll st[MAXN];
        #define ls nd[x].ch[0]
        #define rs nd[x].ch[1]
        inline bool nroot(ll x)
        {
            return nd[nd[x].fa].ch[0]==x||nd[nd[x].fa].ch[1]==x;
        }
        inline ll get(ll x,ll y)
        {
            return nd[x].val<nd[y].val?x:y;
        }
        inline void update(ll x)
        {
            nd[x].mn=get(x,get(nd[ls].mn,nd[rs].mn));
        }
        inline void reverse(ll x)
        {
            swap(ls,rs),nd[x].rv^=1;
        }
        inline void spread(ll x)
        {
            if(nd[x].rv)
            {
                ls?reverse(ls):(void)(1),rs?reverse(rs):(void)(1);
                nd[x].rv=0;
            }
        }
        inline void rotate(ll x)
        {
            ll fa=nd[x].fa,gfa=nd[fa].fa;
            ll dir=nd[fa].ch[1]==x,son=nd[x].ch[!dir];
            if(nroot(fa))
            {
                nd[gfa].ch[nd[gfa].ch[1]==fa]=x;
            }
            nd[x].ch[!dir]=fa,nd[fa].ch[dir]=son;
            if(son)
            {
                nd[son].fa=fa;
            }
            nd[fa].fa=x,nd[x].fa=gfa,update(fa);
        }
        inline void splay(ll x)
        {
            ll fa=x,gfa,cur=0;
            st[++cur]=fa;
            while(nroot(fa))
            {
                st[++cur]=fa=nd[fa].fa;
            }
            while(cur)
            {
                spread(st[cur--]);
            }
            while(nroot(x))
            {
                fa=nd[x].fa,gfa=nd[fa].fa;
                if(nroot(fa))
                {
                    rotate((nd[fa].ch[0]==x)^(nd[gfa].ch[0]==fa)?x:fa);
                }
                rotate(x);
            }
            update(x);
        }
        inline void access(ll x)
        {
            for(register int i=0;x;x=nd[i=x].fa)
            {
                splay(x),rs=i,update(x);
            }
        }
        inline void makeRoot(ll x)
        {
            access(x),splay(x),reverse(x);
        }
        inline void split(ll x,ll y)
        {
            makeRoot(x),access(y),splay(y);
        }
        inline void link(ll x)
        {
            makeRoot(ed[x].from);
            nd[nd[ed[x].from].fa=x+offset].fa=ed[x].to;
            nd[x+offset].val=ed[x].dist,update(x);
        }
        inline void cut(ll x)
        {
            access(ed[x-offset].from),splay(x);
            ls=rs=nd[ls].fa=nd[rs].fa=0,update(x);
        }
        #undef ls
        #undef rs
    };
}
LCT::LinkCutTree lct;
int main()
{
    n=read(),m=read();
    for(register int i=1;i<=m;i++)
    {
        ed[i].from=read(),ed[i].to=read(),ed[i].dist=read();
    }
    for(register int i=0;i<=n;i++)
    {
        ffa[i]=i,lct.nd[i].val=0x7fffffff;
    }
    sort(ed+1,ed+m+1);
    for(register int i=1;i<=m;i++)
    {
        if(find(x=ed[i].from)!=find(y=ed[i].to))
        {
            vis[i]=1,lct.link(i),cur++,ffa[ffa[y]]=ffa[x];
            if(cur==n-1)
            {
                res=ed[i].dist-ed[ptr].dist;
            }
        }
        else
        {
            if(x==y)
            {
                continue;
            }
            lct.split(x,y),vis[lct.nd[y].mn-offset]=0;
            lct.cut(lct.nd[y].mn),lct.link(i),vis[i]=1;
            while(!vis[ptr])
            {
                ptr++;
            }
            if(cur==n-1)
            {
                res=min(res,ed[i].dist-ed[ptr].dist);
            }
        }
    }
    printf("%d
",res);
}
原文地址:https://www.cnblogs.com/Karry5307/p/13062494.html