双关键字最小生成树,我们的大体思路就是,先将a从小到大排序,然后加边,如果两个端点不在同一连通块,直接连,因为lct只能维护点权,所以我们将每条边拆成点,在这道题里,我们边的编号不变,点的编号都加上m。如果在同一个连通块,那么看一下这两个点路径上最大的b值,如果路径上最大的b要大于我们当前这条边的b值就用当前边替换一下,每次判断1,n是否连通,如果能则更新答案。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define lc c[x][0]
#define rc c[x][1]
using namespace std;
const int N=5e4+5;
const int M=1e5+5;
int c[M*5][2],f[M*5],mx[M*5],r[M*5];
int n,m,fa[5*M],u,v,f1,f2,ans=100001,x,y,z;
struct node{
int u,v,a,b;
};
node e[M*5];//数组起码要开大点,不然会锅
void R(int &x){
int t=0; char ch;
for (ch=getchar();!('0'<=ch&&ch<='9');ch=getchar());
for (;('0'<=ch&&ch<='9');ch=getchar()) t=t*10+ch-'0';
x=t;
}
bool cmb(node a,node b){
return a.a<b.a;
}
int find(int x){
if (x==fa[x]) return x;
fa[x]=find(fa[x]);
return fa[x];
}
bool wh(int x){
return x==c[f[x]][1];
}
bool nroot(int x){
return x==c[f[x]][0]||x==c[f[x]][1];
}
void pushr(int x){
swap(lc,rc); r[x]^=1;
}
void pushdown(int x){
if (r[x]){
if (lc) pushr(lc);
if (rc) pushr(rc);
r[x]=0;
}
}
void update(int x){
mx[x]=x;
if ( e[mx[x]].b < e[mx[lc]].b) mx[x]=mx[lc];
if ( e[mx[x]].b < e[mx[rc]].b) mx[x]=mx[rc];
}
void rotate(int x){
int y=f[x],z=f[y],k=wh(x),w=c[x][1^k];
if (nroot(y)) c[z][wh(y)]=x; f[x]=z;
c[y][k]=w; if (w) f[w]=y;
c[x][1^k]=y; f[y]=x;
update(y); update(x);
}
void pd(int x){
if (nroot(x)) pd(f[x]);
pushdown(x);
}
void splay(int x){
pd(x);
for (;nroot(x);rotate(x))
if (nroot(f[x]))
rotate(wh(f[x])==wh(x)?f[x]:x);
}
void access(int x){
for (int y=0;x;x=f[y=x])
splay(x),rc=y,update(x);
}
void make(int x){
access(x); splay(x); pushr(x);
}
void split(int x,int y){
make(x); access(y); splay(y);
}
void link(int x,int y){
make(x); f[x]=y;
}
void cut(int x,int y){ // 如果保证原来有边,这样打常数更小,link也一样
split(x,y); f[x]=c[y][0]=0;
update(y);
}
int main(){
//freopen("jzoj3754.in","r",stdin);
//freopen("jzoj3754.out","w",stdout);
R(n);R(m);
fo(i,1,m){
R(e[i].u); R(e[i].v); R(e[i].a); R(e[i].b);
e[i].u+=m; e[i].v+=m;
}
sort(e+1,e+m+1,cmb);
// fo(i,1,m) printf("%d %d %d %d\n",e[i].u,e[i].v,e[i].a,e[i].b);
// return 0;
fo(i,1,n) fa[i+m]=i+m;
fo(i,1,m){
x=e[i].u; y=e[i].v;
if (x==y) continue;
f1=find(x); f2=find(y);
if (f1!=f2){
fa[f1]=f2;
link(x,i); link(i,y);
}
else{
split(x,y); z=mx[y]; // 第一次cut之后mx[y]可能会变,所以要记录下来,否则第二次cut就会出锅
if (e[z].b>e[i].b) {
cut(e[z].u,z);
cut(e[z].v,z);
link(x,i); link(i,y);
}
}
if (find(1+m)==find(n+m)){
split(1+m,n+m);
ans=min(ans,e[i].a+e[mx[n+m]].b);
}
}
if (ans==100001) printf("%d",-1);
else printf("%d",ans);
return 0;
}