传送门

首先将边排序,依次尝试加边。两个点不连通就连,否则把这条边取代两点之间权值最小的边。可以用$LCT$拆边为点维护。

考虑这种做法的正确性:

从小到大排序可以理解为枚举答案生成树的最大值(比它大的都在后面还没有加)。同时最小值也要尽量大,因此用较大的边替换较小的边一定更优。同时之前枚举的边也是用较大的边替换较小的边,也就最大化了最小值。

这样只要当前已经构成了生成树,就可以开始更新答案了。最大边就是当前枚举的边,最小边可以用平衡树维护。(一开始用的$multiset$,啥都没想直接$erase$,没想到会把所有的元素都清除掉,调了半小时。。。后来还是写的$treap$)

细节上注意数据中有自环,要判断一下。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>

#define maxn 300005
#define inf 0x3f3f3f3f
#define pn putchar('\n')
#define px(x) putchar(x)
#define ps putchar(' ')
#define pd puts("======================")
#define pj puts("++++++++++++++++++++++")

using namespace std;

inline int read(){
    int x=0,y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
template<typename T>
inline T read(){
    T x=0;
    int y=0;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return y?-x:x;
}
inline int ran(){
    static int seed=45562;
    return seed=(seed*48271LL%2147483647);
}//优化常数的随机数函数
int n,m;
struct edge{
    int from,to,l;
}e[maxn];
struct Link_Cut_Tree{
    int mi[maxn],dat[maxn],son[maxn][2],fa[maxn],rev[maxn],st[maxn];
    //dat是这个点(边)的权值,mi是权值最小的点的序号
    //边的序号+n为它拆成的点
#define son(x,y) son[x][y]
#define whson(x) (son[fa[x]][1]==x)
#define root(x) (son[fa[x]][0]!=x&&son[fa[x]][1]!=x)
    inline int MIN(int x,int y){
        return dat[x]<dat[y]?x:y;
    }
    inline void update(int node){
        mi[node]=MIN(node,MIN(mi[son(node,0)],mi[son(node,1)]));
    }
    inline void addedge(int s,int f,int wh){
        if(s)fa[s]=f;
        son(f,wh)=s;
    }
    inline void reverdown(int node){
        swap(son(node,0),son(node,1));
        rev[node]^=1;
    }
    inline void pushdown(int node){
        if(rev[node]){
            if(son(node,0))reverdown(son(node,0));
            if(son(node,1))reverdown(son(node,1));
            rev[node]=0;
        }
    }
    inline void zhuan(int x){
        int f=fa[x],gf=fa[f],wh=whson(x);
        fa[x]=gf;
        if(!root(f))son(gf,whson(f))=x;
        addedge(son(x,wh^1),f,wh);
        addedge(f,x,wh^1);
        update(f),update(x);
    }
    inline void splay(int x){
        int top=1,y=x;
        st[1]=x;
        while(!root(y))st[++top]=y=fa[y];
        while(top)pushdown(st[top--]);
        while(!root(x)){
            y=fa[x];
            if(!root(y))
                zhuan(whson(x)^whson(y)?x:y);
            zhuan(x);
        }
    }
    void access(int x){
        for(int y=0;x;y=x,x=fa[x])
            splay(x),son(x,1)=y,update(x);
    }
    void makeroot(int x){
        access(x),splay(x),reverdown(x);
    }
    void link(int x,int y){
        makeroot(x);
        fa[x]=y;
    }
    void cut(int x,int y){
        makeroot(x),access(y),splay(y);
        son(y,0)=fa[x]=0;
        update(y);
    }
    int findroot(int x){
        access(x),splay(x);
        while(son(x,0))pushdown(x),x=son(x,0);
        return x;
    }
    int Get(int x,int y){
        makeroot(x),access(y),splay(y);
        return mi[y];
    }
    Link_Cut_Tree(){
        memset(dat,0x3f,sizeof dat);
    }
}lct;
struct Treap{
    int dat[maxn],ls[maxn],rs[maxn],ra[maxn],root,cnt;
#define ls(x) ls[x]
#define rs(x) rs[x]
    void right(int &node){
        int rec=ls(node);
        ls(node)=rs(rec);
        rs(rec)=node;
        node=rec;
    }
    void left(int &node){
        int rec=rs(node);
        rs(node)=ls(rec);
        ls(rec)=node;
        node=rec;
    }
    void insert(int &node,int d){
        if(!node){
            node=++cnt;
            ra[node]=ran();
            dat[node]=d;
            return;
        }
        if(dat[node]<d){
            insert(rs(node),d);
            if(ra[rs(node)]>ra[node])left(node);
        }
        else {
            insert(ls(node),d);
            if(ra[ls(node)]>ra[node])right(node);
        }
    }
    void del(int &node,int d){
        if(dat[node]==d){
            if(ls(node)&&rs(node)){
                if(ra[ls(node)]>ra[rs(node)])right(node),del(rs(node),d);
                else left(node),del(ls(node),d);
            }
            else node=ls(node)+rs(node);
            return;
        }
        if(dat[node]<d)del(rs(node),d);
        else del(ls(node),d);
    }
    int getmin(){
        int node=root;
        while(ls(node))node=ls(node);
        return dat[node];
    }
}tr;
inline bool cmp(edge x,edge y){
    return x.l<y.l;
}
void solve(){
    int ans=inf,cnt=1;
    for(register int i=1;i<=n;++i)
        lct.mi[i]=i;
    for(register int i=n+1;i<=n+m;++i)
        lct.dat[i]=e[i-n].l,lct.mi[i]=i;
    //lct初始化
    for(register int i=1;i<=m;++i){
        int x=e[i].from,y=e[i].to;
        if(x==y)continue;//自环判断
        tr.insert(tr.root,e[i].l);
        if(lct.findroot(x)==lct.findroot(y)){
            int k=lct.Get(x,y);
            lct.cut(x,k),lct.cut(y,k);
            tr.del(tr.root,e[k-n].l);
        }
        else ++cnt;
        lct.link(x,i+n),lct.link(i+n,y);
        if(cnt==n)ans=min(ans,e[i].l-tr.getmin());
        //已经构成树,统计答案
    }
    printf("%d\n",ans);
}
int main(){
    n=read(),m=read();
    for(register int i=1;i<=m;++i)
        e[i].from=read(),e[i].to=read(),e[i].l=read();
    sort(e+1,e+1+m,cmp);
    solve();
}