题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3669
思路
这个题本来是个SPFA的题。。。但是正解不好想,倒是LCT的暴力比较好想。。。
用Kruscal的方法时刻维护一个生成树,按照a升序加边,并用LCT维护这个生成树,具体做法就是把每个边看成是LCT的结点,点也看成是LCT的结点,加入一条边时,就link一个点和边,link另一个点和边,但是与Kruscal不同的是,如果当前的边的两个端点已经联通了,但是这两个端点路径上的b最大的边比当前的边的b值大,仍然可以加入这条边,并把原来两个端点路径上b最大的那条边cut掉,在上述过程中,如果任何时候发现起点和终点已经联通,就更新答案。。。
代码
其实我只用了半个小时打好LCT,结果一直过不了样例,以为LCT写错了。。。搞了半天才发现LCT没问题,是我先对LCT结点标记初始化了,再给边排的序。。。我靠。。。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 200000
#define lson ch[o][0]
#define rson ch[o][1]
using namespace std;
int ch[MAXN][2],fa[MAXN],maxtag[MAXN],val[MAXN]; //maxtag[i]=区间最大值的对应子树根节点编号!!!
int stack[MAXN],top=0;
bool rev[MAXN];
bool isRoot(int o)
{
return ch[fa[o]][0]!=o&&ch[fa[o]][1]!=o;
}
void pushup(int o)
{
maxtag[o]=o;
if(val[maxtag[lson]]>val[maxtag[o]]) maxtag[o]=maxtag[lson];
if(val[maxtag[rson]]>val[maxtag[o]]) maxtag[o]=maxtag[rson];
}
void pushdown(int o)
{
//if(!o) return;
if(rev[o])
{
rev[lson]^=1;
rev[rson]^=1;
swap(lson,rson);
rev[o]=false;
}
}
void rot(int x)
{
int y=fa[x],z=fa[y];
int p,q;
if(ch[y][0]==x) p=0;
else p=1;
q=p^1;
if(!isRoot(y))
{
if(ch[z][0]==y) ch[z][0]=x;
else ch[z][1]=x;
}
fa[x]=z,fa[y]=x;
ch[y][p]=ch[x][q];
fa[ch[x][q]]=y;
ch[x][q]=y;
pushup(y);
pushup(x);
}
void splay(int x)
{
top=0;
stack[++top]=x;
for(int i=x;!isRoot(i);i=fa[i]) stack[++top]=fa[i];
for(int i=top;i>=1;i--) pushdown(stack[i]); //!!!!!!
while(!isRoot(x))
{
int y=fa[x],z=fa[y];
if(!isRoot(y))
{
if((ch[y][0]==x)==(ch[z][0]==y)) rot(y);
else rot(x);
}
rot(x);
}
}
void access(int x)
{
int tmp=0;
while(x)
{
splay(x);
ch[x][1]=tmp;
pushup(x);
tmp=x;
x=fa[x];
}
}
void makeroot(int x)
{
access(x);
splay(x);
rev[x]^=1;
}
void link(int x,int y) //y是x父亲
{
makeroot(x);
fa[x]=y;
}
void cut(int x,int y) //y是x父亲
{
makeroot(x);
access(y);
splay(y);
if(ch[y][0]==x) ch[y][0]=fa[x]=0;
}
int query(int x,int y) //询问路径xy上的最大边
{
makeroot(x);
access(y);
splay(y);
return maxtag[y];
}
int f[MAXN];
int findSet(int x)
{
if(f[x]==x) return f[x];
return f[x]=findSet(f[x]);
}
struct edge
{
int u,v,a,b;
}edges[MAXN];
int n,m;
bool cmp(edge a,edge b)
{
return a.a<b.a;
}
void solve(int i) //在已经联通的块中加入边i
{
int old=query(edges[i].u,edges[i].v);
if(val[old]>edges[i].b)
{
cut(edges[old-n].u,old);
cut(edges[old-n].v,old);
link(edges[i].u,n+i);
link(edges[i].v,n+i);
}
}
void work()
{
for(int i=0;i<MAXN;i++) f[i]=i;
int ans=0x3f3f3f3f;
for(int i=1;i<=m;i++)
{
int rootu=findSet(edges[i].u),rootv=findSet(edges[i].v);
if(rootu!=rootv)
{
f[rootu]=rootv;
link(edges[i].u,n+i);
link(edges[i].v,n+i);
}
else solve(i);
if(findSet(1)==findSet(n)) //起点和终点已经在MST中联通
ans=min(ans,val[query(1,n)]+edges[i].a);
}
if(ans!=0x3f3f3f3f) printf("%d\n",ans);
else printf("-1\n");
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d%d",&edges[i].u,&edges[i].v,&edges[i].a,&edges[i].b);
sort(edges+1,edges+m+1,cmp); //!!!!!fuck!!!!!
for(int i=1;i<=m;i++)
{
val[n+i]=edges[i].b;
maxtag[n+i]=n+i;
}
work();
return 0;
}