/****************************************\
* Author : ztx
* Title : [NOI2014]魔法森林
* ALG : LCT+Kruskal
* CMT :
* 把边按a从小到大排序
* 然后一条条加边
* 动态维护b的最小生成树
* 假设现在加入的边是(x,y) a b
* 那么答案=min(答案,a+树上1到n路径上的b的最大值)
* Time :
\****************************************/
/****************************************\
* Author : ztx
* Title : [NOI2014]魔法森林
* ALG : LCT+Kruskal
* CMT :
* 把边按a从小到大排序
* 然后一条条加边
* 动态维护b的最小生成树
* 假设现在加入的边是(x,y) a b
* 那么答案=min(答案,a+树上1到n路径上的b的最大值)
* Time :
\****************************************/
#include <cstdio>
#include <algorithm>
int CH , NEG ;
inline int GETCHAR() {
static const int LEN = 1<<15 ;
static char BUF[LEN] , *S = BUF , *T = BUF ;
if (S == T) {
T = (S=BUF)+fread(BUF , 1 , LEN , stdin) ;
if (S == T) return EOF ;
}
return *S ++ ;
}
inline void read(int& ret) {
ret = 0 ; while (CH=GETCHAR() , CH<'!') ;
while (ret = (ret<<1)+(ret<<3)+CH-'0' , CH=GETCHAR() , CH>'!') ;
}
#define maxn 50010LL
#define maxm 100010LL
#define maxt 150010LL
#define infi 0x7f7f7f7fLL
int n , m , Q , ans = infi , ansa ;
struct EDGE { int u , v , a , b ; } e[maxm] ;
bool Cmpa(const EDGE& a , const EDGE& b) {
if (a.a == b.a) return a.b < b.b ;
else return a.a < b.a ;
}
int fa[maxt] = {0} , ch[maxt][2] = {0} ;
int maxb[maxt] = {0}/*Here are the labels stored*/ , val[maxt] = {0} ;
bool rev[maxt] = {0} ;
#define null 0LL
#define left(u) ch[u][0]
#define right(u) ch[u][1]
inline void Exchange(int& a , int& b) { int c = a ; a = b ; b = c ; }
inline bool Isrt(int u) { return (fa[u]==null)||(left(fa[u])!=u&&right(fa[u])!=u) ; }
inline void Maintain(int u) {
maxb[u] = u ;
if (left(u) && val[maxb[left(u)]]>val[maxb[u]]) maxb[u] = maxb[left(u)] ;
if (right(u) && val[maxb[right(u)]]>val[maxb[u]]) maxb[u] = maxb[right(u)] ;
}
inline void Clear(int u) {
if (!u) return ;
if (!Isrt(u)) Clear(fa[u]) ;
if (rev[u]) {
rev[u] = false ;
rev[left(u)] ^= true ;
rev[right(u)] ^= true ;
Exchange(left(u) , right(u)) ;
}
}
inline void zig(int x) {
/* right rotate */
int y = fa[x] , z = fa[y] ;
if (left(z) == y) left(z) = x ; else if(right(z) == y) right(z) = x ; fa[x] = z ;
fa[right(x)] = y ; left(y) = right(x) ; fa[y] = x ; right(x) = y ;
Maintain(y) , Maintain(x) ;
}
inline void zag(int x) {
/* left rotate */
int y = fa[x] , z = fa[y] ;
if (left(z) == y) left(z) = x ; else if(right(z) == y) right(z) = x ; fa[x] = z ;
fa[left(x)] = y ; right(y) = left(x) ; fa[y] = x ; left(x) = y ;
Maintain(y) , Maintain(x) ;
}
inline void Splay(int x) {
Clear(x) ;
int y , z ;
while (!Isrt(x)) {
y = fa[x] , z = fa[y] ;
if (Isrt(y)) {
if (left(y) == x) zig(x) ; else zag(x) ;
} else {
if (left(z) == y) {
if (left(y) == x) zig(x) , zig(x) ;
else zag(x) , zig(x) ;
} else if (right(z) == y) {
if (right(y) == x) zag(x) , zag(x) ;
else zig(x) , zag(x) ;
}
}
}
}
inline void Access(int u) {
for (int v = null ; u ; u = fa[u]) Splay(u) , right(u) = v , v = u , Maintain(u) ;
}
inline void Makeroot(int u) {
Access(u) , Splay(u) , rev[u] ^= true ;
}
inline void Link(int u , int v) {
Makeroot(u) , fa[u] = v ;
}
inline void Cut(int u , int v) {
Makeroot(u) ; Access(v) , Splay(v) , fa[u] = left(v) = null ;
}
inline int Query(int u , int v) {
Makeroot(u) ; Access(v) , Splay(v) ; return maxb[v] ;
}
int f[maxn] = {0} ;
inline int GetAnc(int u) { return f[u] ? f[u] = GetAnc(f[u]) : u ; }
inline bool Union(int u , int v) {
u = GetAnc(u) , v = GetAnc(v) ;
if (u == v) return false ;
f[u] = v ; return true ;
}
int i , j , cnt = 0 ;
int main() {
#define READ
#ifdef READ
freopen("magicalforest.in" ,"r",stdin ) ;
freopen("magicalforest.out","w",stdout) ;
#endif
read(n) , read(m) ;
for (i = 1 ; i <= m ; i ++ ) {
read(e[i].u) , read(e[i].v) , read(e[i].a) , read(e[i].b) ;
}
std::sort(e+1 , e+m+1 , Cmpa) ;
for (i = 1 ; i <= m ; i ++ ) {
val[i+n] = e[i].b , maxb[i+n] = i+n ;
/* Edges are regarded as points */
}
/* Kruskal */
cnt = 0 ;
for (i = 1 ; i <= m ; i ++ ) {
if (Union(e[i].u , e[i].v)) {
Link(e[i].u , i+n) , Link(e[i].v , i+n) ;
} else {
j = Query(e[i].u , e[i].v) ;
if (e[i].b < val[j]) {
Cut(e[j-n].u , j) , Cut(e[j-n].v , j) ;
Link(e[i].u , i+n) , Link(e[i].v , i+n) ;
}
}
if (GetAnc(1) == GetAnc(n)) {
ansa = val[Query(1 , n)]+e[i].a ;
if (ansa < ans) ans = ansa ;
}
}
if (ans == infi) ans = -1 ;
printf("%d\n", ans ) ;
#ifdef READ
fclose(stdin) ; fclose(stdout) ;
#else
getchar() ; getchar() ;
#endif
return 0 ;
}
/*
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
3 1
1 2 1 1
*/