Description
给出一个(n(nleq5 imes10^4))个点(m(mleq10^5))条边的无向图,每条边有两个权值(a,b)。求所有从(1)到(n)的路径中,路径上(max{a}+max{b})的最小值。
Solution
如果说(max{a}+max{b})的最小值不好求的话,那么就可以固定(max{a})后求(max{b})的最小值。
那么如果无向图的边权只有(b),怎么求(max{b})的最小值呢?实际上,这条最小的路径就是这个无向图的MST上的路径((1,n))。考虑Kruskal的过程就能知道不存在更优的路径了。
于是我们将边按(a)从小到大的顺序依次加入图中,并同时维护以(b)为权值的MST,若(1,n)连通则求(a)与此时路径((1,n))上的(max)之和。
这个问题可以用lct解决:加入边((u,v))时,若(u,v)不连通则link(u,v)
;若(u,v)连通则查询路径((u,v))上的最大值,如果该值大于边((u,v))的权值就断掉这条边并link(u,v)
,如果不大于就什么也不做。求路径上的(max)就不用说了。
在这个lct中,我们要维护的是边权。对于需要维护边权的lct,我们可以开(n+m)个节点,其中(n)个代表点,(m)个代表边。将原树中的“点-点”都视为“点-边-点”,然后就可以和普通的lct一样维护啦。不过link
和cut
时需要考虑到边的影响。
时间复杂度(O(mlog(n+m)))。
Code
//[Noi2014]魔法森林
#include <cstdio>
#include <algorithm>
using namespace std;
inline char gc()
{
static char now[1<<16],*S,*T;
if(S==T) {T=(S=now)+fread(now,1,1<<16,stdin); if(S==T) return EOF;}
return *S++;
}
inline int read()
{
int x=0; char ch=gc();
while(ch<'0'||'9'<ch) ch=gc();
while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
return x;
}
int const N=2e5+10;
int const INF=0x7FFFFFFF;
int n,m;
struct edge{int u,v,a,b;} ed[N];
bool cmpA(edge x,edge y) {return x.a<y.a;}
int pre[N];
int find(int x) {return x==pre[x]?x:pre[x]=find(pre[x]);}
int fa[N],ch[N][2]; int val[N],maxB[N]; bool rev[N];
int wh(int p) {return p==ch[fa[p]][1];}
bool notRt(int p) {return p==ch[fa[p]][wh(p)];}
void rever(int p) {rev[p]^=1,swap(ch[p][0],ch[p][1]);}
void reset(int p) {fa[p]=ch[p][0]=ch[p][1]=0; maxB[p]=p; rev[p]=false;}
void update(int p)
{
maxB[p]=p;
if(val[maxB[ch[p][0]]]>val[maxB[p]]) maxB[p]=maxB[ch[p][0]];
if(val[maxB[ch[p][1]]]>val[maxB[p]]) maxB[p]=maxB[ch[p][1]];
}
void pushdw(int p) {if(rev[p]) rever(ch[p][0]),rever(ch[p][1]),rev[p]=false;}
void rotate(int p)
{
int q=fa[p],r=fa[q],w=wh(p);
fa[p]=r; if(notRt(q)) ch[r][wh(q)]=p;
fa[ch[q][w]=ch[p][w^1]]=q;
fa[ch[p][w^1]=q]=p;
update(q),update(p);
}
void pushdwRt(int p) {if(notRt(p)) pushdwRt(fa[p]); pushdw(p);}
void splay(int p)
{
pushdwRt(p);
for(int q=fa[p];notRt(p);rotate(p),q=fa[p]) if(notRt(q)) rotate(wh(p)^wh(q)?p:q);
}
void access(int p) {for(int q=0;p;q=p,p=fa[p]) splay(p),ch[p][1]=q,update(p);}
void makeRt(int p) {access(p),splay(p),rever(p);}
void path(int p,int q) {makeRt(p),access(q),splay(q);}
void link(int i) {int p=ed[i].u,q=ed[i].v; makeRt(p); fa[p]=n+i,fa[n+i]=q;}
void cut(int i) {int p=ed[i].u,q=ed[i].v; path(p,q); fa[p]=0,ch[q][0]=0,update(q);}
int main()
{
n=read(),m=read();
for(int i=1;i<=m;i++) ed[i].u=read(),ed[i].v=read(),ed[i].a=read(),ed[i].b=read();
sort(ed+1,ed+m+1,cmpA);
int ans=INF;
for(int i=1;i<=n;i++) pre[i]=i;
for(int i=1;i<=m;i++) val[n+i]=ed[i].b,maxB[n+i]=n+i;
for(int i=1;i<=m;i++)
{
int u=ed[i].u,v=ed[i].v;
if(find(u)!=find(v)) link(i),pre[find(u)]=find(v);
else {path(u,v); if(val[maxB[v]]>ed[i].b) cut(maxB[v]-n),link(i);}
if(find(1)==find(n)) path(1,n),ans=min(ans,ed[i].a+val[maxB[n]]);
}
printf("%d
",ans<INF?ans:-1);
return 0;
}
P.S.
我这两天又有点咸了...