来自蒟蒻 \(Hero \_of \_Someone\) 的 \(LCT\) 学习笔记
$
$
有一个很好的做法是 \(spfa\) ,但是我们不聊 \(spfa\) , 来聊 \(LCT\)
\(LCT\) 做法跟 \(spfa\) 的做法其实有点像,
先将所有的边按 \(a\) 的值从小到大排, 再以 \(b\) 的值为边权来动态的维护最小生成树,
答案即为 当前插入边的 \(a\) 值加上最小生成树中的最大边权 的最小值
$
$
此外, 用 \(LCT\) 维护 \(MST\) , 就是在添边的时候如果遇到环且环上最长的边边权大于当前边, 就将最大边 \(cut\) , 再将当前边添入
//made by Hero_of_Someone
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define N (50040)
#define M (100010)
#define RG register
using namespace std;
inline int gi(){ RG int x=0,q=1; RG char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') q=-1,ch=getchar(); while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=getchar(); return q*x; }
void File(){freopen(".in","r",stdin);freopen(".out","w",stdout);}
int n,m;
struct Edge{
int u,v,a,b;
bool operator<(const Edge& x)const{ return a<x.a; }
}e[M];
inline void init(){
n=gi(),m=gi();
for(RG int i=1;i<=m;i++){
e[i].u=gi(),e[i].v=gi();
e[i].a=gi(),e[i].b=gi();
}
sort(e+1,e+m+1);
}
int Max[N+M],val[N+M];
int ch[N+M][2],fa[N+M],rev[N+M];
inline void cur(int x,int y){ val[x]=Max[x]=y; }
inline bool cnm(int x,int y){ return e[x].b<e[y].b; }
inline void up(int x){
Max[x]=max(Max[ch[x][0]],Max[ch[x][1]],cnm);
Max[x]=max(Max[x],val[x],cnm);
}
inline void reverse(int x){
swap(ch[x][0],ch[x][1]);
rev[x]^=1;
}
inline void down(int x){
if(!rev[x]) return ;
reverse(ch[x][0]);
reverse(ch[x][1]);
rev[x]=0;
}
inline bool is_root(int x){ return ch[fa[x]][0]!=x && x!=ch[fa[x]][1]; }
inline bool lr(int x){ return x==ch[fa[x]][1]; }
inline void rotate(int x){
RG int y=fa[x],z=fa[y],k=lr(x);
if(!is_root(y)) ch[z][lr(y)]=x;
fa[x]=z; fa[ch[x][k^1]]=y; fa[y]=x;
ch[y][k]=ch[x][k^1]; ch[x][k^1]=y;
up(y); up(x);
}
int st[N+M];
inline void splay(int x){
RG int y=x,top=0;
while(1){
st[++top]=y;
if(is_root(y)) break;
y=fa[y];
}
for(RG int i=top;i;i--) down(st[i]);
while(!is_root(x)){
if(!is_root(fa[x])) rotate(lr(x)^lr(fa[x])?x:fa[x]);
rotate(x);
}
}
inline void access(int x){
RG int y=0;
while(x){ splay(x);
ch[x][1]=y; fa[y]=x;
up(x); y=x; x=fa[x];
}
}
inline void make_root(int x){
access(x); splay(x); reverse(x);
}
inline int find(int x){
while(fa[x]) x=fa[x];
return x;
}
inline void link(int x,int y){
if(find(x)==find(y)) return ;
make_root(x); fa[x]=y;
}
inline void cut(int x,int y){
make_root(x); access(y); splay(y);
if(ch[y][0]==x) ch[y][0]=0,fa[x]=0,up(y);
}
inline int query(int x,int y){
make_root(x); access(y); splay(y);
return Max[y];
}
inline void Insert(int id){
RG int x=e[id].u,y=e[id].v;
if(x==y) return ;
if(find(x)==find(y)){
RG int tmp=query(x,y);
if(e[tmp].b<=e[id].b) return ;
cut(n+tmp,e[tmp].u);
cut(n+tmp,e[tmp].v);
}
cur(n+id,id);
link(x,n+id);
link(y,n+id);
}
inline void work(){
RG int ans=1<<30;
for(RG int i=1;i<=m;i++){
Insert(i);
if(find(1)!=find(n)) continue;
ans=min(ans,e[i].a+e[query(1,n)].b);
}
if(ans==1<<30) ans=-1;
printf("%d\n",ans);
}
int main(){ init(); work(); return 0; }