和货车运输那道题挺像的。
运用到一个结论:图上两点间路径边权最大值的最小值存在于整个图的最小生成树中。
有了这个结论,我们就要将问题向最小生成树上靠拢。
我们可以将 $c$ 从小到大排序,依次枚举。
这其实是在构建以 $e$ 为关键字的最小生成树。
动态加入对应的边,用 $LCT$ 维护最小生成树即可。
如果两点不联通,这个边显然是要加的。
如果两点联通,考虑一下两种情况:
1)新加入的 $e$ 值大于两点简单路径间最大值,显然不要加( $c$ 是不下降的)
2)新加入的 $e$ 值小于两点简单路径间最大值,是要加入的。
(由于我们研究的是 $1$ 到 $n$ 的路径,每次都 Acess 查一下答案即可)
即使加入了更差,那也只可能是最后一条边会有这样的后果(想一想,为什么)
Code:
#include <cstdio>
#include <algorithm>
#include <cstring>
#define maxn 1000000
#define ll long long
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int n,m,tot,err[maxn];
struct E{ int son,fa,c; }trash[maxn << 1];
struct Edge{ int u,v,c,e; }A[maxn],edges[maxn];
int cmp(Edge a,Edge b){ return a.c < b.c; }
int p[maxn];
void init(){ for(int i=0;i<maxn;++i) p[i]=i;}
int find(int x) { return p[x]==x?x:p[x]=find(p[x]);}
void merge(int x,int y) { int a=find(x),b=find(y); p[a] = b; }
int ch[maxn][2],f[maxn],maxv[maxn],val[maxn],posv[maxn],stac[maxn],tag[maxn];
int lson(int x){ return ch[x][0]; }
int rson(int x){ return ch[x][1]; }
int isRoot(int x){ return (ch[f[x]][1]!=x && ch[f[x]][0]!=x);}
int get(int x){ return ch[f[x]][1]==x;}
void make(int x) { if(x)tag[x]^=1,swap(ch[x][0],ch[x][1]);}
void pushup(int x){
maxv[x]=max(maxv[lson(x)],maxv[rson(x)]);
posv[x]=maxv[lson(x)] > maxv[rson(x)] ? posv[lson(x)] : posv[rson(x)];
if(val[x]>maxv[x]) maxv[x]=val[x],posv[x]=x;
}
void pushdown(int x){ if(tag[x] && x)make(lson(x)),make(rson(x)),tag[x]^=1; }
void rotate(int x){
int old=f[x],oldf=f[old],which=get(x);
if(!isRoot(old)) ch[oldf][ch[oldf][1]==old]=x;
ch[old][which]=ch[x][which^1],f[ch[old][which]]=old;
ch[x][which^1]=old,f[old]=x,f[x]=oldf;
pushup(old),pushup(x);
}
void splay(int x){
int v=0,u=x;
stac[++v]=u;
while(!isRoot(u)) stac[++v]=f[u],u=f[u];
while(v) pushdown(stac[v--]);
u=f[u];
for(int fa;(fa=f[x])!=u;rotate(x))
if(f[fa]!=u) rotate(get(fa)==get(x)?fa:x);
}
void Access(int x) {for(int y=0;x;y=x,x=f[x]) splay(x),ch[x][1]=y,pushup(x);}
void makeRoot(int x){Access(x),splay(x),make(x);}
void link(int x,int y) {makeRoot(x),f[x]=y;}
void split(int x,int y) {makeRoot(x),Access(y),splay(y);}
void cut(int x,int y){makeRoot(x),Access(y),splay(y),ch[y][0]=f[ch[y][0]]=0,pushup(y);}
int main(){
//setIO("input");
init();
scanf("%d%d",&n,&m);
int cc=0;
for(int i=1;i<=m;++i) {
scanf("%d%d%d%d",&A[i].u,&A[i].v,&A[i].c,&A[i].e);
if(A[i].u!=A[i].v) edges[++cc]=A[i];
}
m=cc;
sort(edges+1,edges+1+m,cmp);
int maxc=0,maxe=0;
tot=n+1;
int cnt=0,ans=2e9;
for(int i=1;i<=m;++i) {
int a=edges[i].u,b=edges[i].v;
if(find(a)==find(b)) {
split(a,b);
if(edges[i].e < maxv[b] ) {
int w=posv[b];
cut(w,trash[w].fa);
cut(trash[w].son,w);
err[w]=0;
++tot;
val[tot]=maxv[tot]=edges[i].e;
posv[tot]=tot;
trash[tot].fa=b;
trash[tot].son=a;
trash[tot].c=edges[i].c;
err[tot]=edges[i].e;
link(tot,a),link(b,tot);
maxc=max(maxc,edges[i].c);
}
}
else {
++cnt;
merge(a,b);
++tot;
val[tot]=maxv[tot]=edges[i].e;
trash[tot].fa=b;
trash[tot].son=a;
trash[tot].c=edges[i].c;
posv[tot]=tot;
err[tot]=edges[i].e;
link(a,tot),link(tot,b);
maxc=max(maxc,edges[i].c);
}
if(find(1)==find(n)) {
split(1,n);
ans=min(ans,maxc+maxv[n]);
}
}
for(int i=1;i<=tot;++i) maxe=max(maxe,err[i]);
if(find(1)!=find(n)) printf("-1");
else printf("%d",ans);
return 0;
}