把边按权值
a
a
从小到大插入,并维护权值的最小生成树。具体地,如果link的一条边的两个端点已经联通,那么把这两个点路径上
b
b
最大的边cut掉。最后用到
n
n
路径上的最小值加上当前
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;
}