动态加点spfa点这里
题目大意:
给定一个图,每条边有两个权值,ai和bi,求一条路径使得这条路径上的边的amax+bmax
最小。
思路:
若只有一种权值,求出此图的MST后即可得到答案,考虑两种权值的情况,可以从小到大枚举一种权值,并按照第一种权值从小到大的顺序动态加边维护MST,用lct维护即可。
考虑如何将边权在lct中表示,我们可以将每一条边表示成一个点,连接这条边两边的端点即可,表示边的点的权值即为bi,表示点的点只起到连接作用,所以权值统一设成0.。
若新加的这条边连接不成环则直接连接,若成环,则删掉换上的大于此边的权值的最大的边,所以splay中维护的不仅有最大值,还有最大值的编号。
/*==============================
* Author : Ylsoi
* Problem : [NOI2014]magic
* File : [NOI2014]magic_lct.cpp
* Algorithm : MST,Link Cut Tree
* Time : 2018.4.9
* ============================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
void File(){
freopen("[NOI2014]magic_lct.in","r",stdin);
freopen("[NOI2014]magic_lct.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=5e4+10;
const int maxm=1e5+10;
int n,m,cnt;
struct edge{
int u,v,a,b;
bool operator < (const edge &tt) const {
return a<tt.a;
}
}E[maxm];
int Max(int _,int __){return _>__ ? _ : __;}
int Min(int _,int __){return _<__ ? _ : __;}
int dis[maxn],ans=inf;
bool vis[maxn];
struct lct{
#define lc ch[rt][0]
#define rc ch[rt][1]
#define rel(x) (ch[fa[x]][1]==x)
int va[maxn+maxm],Maxva[maxn+maxm],Maxnum[maxn+maxm],fa[maxn+maxm],ch[maxn+maxm][2],q[maxn+maxm],top;
bool tag[maxn+maxm];
bool isrt(int rt){return ch[fa[rt]][0]!=rt && ch[fa[rt]][1]!=rt;}
void pushup(int rt){
Maxva[rt]=Max(va[rt],Max(Maxva[lc],Maxva[rc]));
if(Maxva[rt]==va[rt])Maxnum[rt]=rt;
else if(Maxva[rt]==Maxva[lc])Maxnum[rt]=Maxnum[lc];
else Maxnum[rt]=Maxnum[rc];
}
void pushdown(int rt){if(tag[rt])tag[rt]^=1,tag[lc]^=1,tag[rc]^=1,swap(lc,rc);}
void rotate(int rt){
int f=fa[rt],r=rel(rt);
fa[rt]=fa[f];if(!isrt(f))ch[fa[f]][rel(f)]=rt;
fa[ch[rt][r^1]]=f;ch[f][r]=ch[rt][r^1];
fa[f]=rt;ch[rt][r^1]=f;
pushup(f);pushup(rt);
}
void splay(int rt){
top=1;q[top]=rt;
int u=rt;
while(!isrt(u))u=fa[u],q[++top]=u;
DREP(i,top,1)pushdown(q[i]);
while(!isrt(rt)){
int f=fa[rt];
if(!isrt(f))rotate(rel(f)==rel(rt) ? f : rt);
rotate(rt);
}
}
void access(int rt){for(int las=0;rt;las=rt,rt=fa[rt])splay(rt),rc=las,pushup(rt);}
void makert(int rt){access(rt);splay(rt);tag[rt]^=1;}
int findrt(int rt){access(rt);splay(rt);while(lc)rt=lc;return rt;}
bool con(int x,int y){makert(x);return (findrt(y)==x);}
void split(int x,int y){makert(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);if(ch[y][0]==x)ch[y][0]=x,fa[x]=0,pushup(y);}
void link(int x,int y){if(con(x,y))return;makert(x);fa[x]=y;}
int query(int x,int y){if(!con(x,y))return inf;split(x,y);return Maxva[y];}
}T;
int main(){
File();
scanf("%d%d",&n,&m);
REP(i,1,m){
int u,v,a,b;
scanf("%d%d%d%d",&u,&v,&a,&b);
++cnt;
E[cnt].u=u;E[cnt].v=v;
E[cnt].a=a;E[cnt].b=b;
}
sort(E+1,E+cnt+1);
REP(i,1,n)T.va[i]=0,T.Maxnum[i]=i;
REP(i,n+1,n+m)T.va[i]=E[i-n].b,T.Maxnum[i]=i;
int p=1;
while(p<=m){
int i=p;
while(E[i].a==E[p].a){
int u=E[i].u,v=E[i].v;
if(!T.con(u,v)){
T.link(i+n,u);
T.link(i+n,v);
}
else{
T.split(u,v);
int num=T.Maxnum[v];
if(T.va[num]>E[i].b){
T.cut(E[num-n].u,num);
T.cut(E[num-n].v,num);
T.link(u,i+n);
T.link(v,i+n);
}
}
++i;
}
ans=Min(ans,T.query(1,n)+E[p].a);
p=i;
}
if(ans!=inf)printf("%d\n",ans);
else printf("-1\n");
return 0;
}