BZOJ3669 - [Noi2014]魔法森林

Portal

Description

给出一个\(n(n\leq5\times10^4)\)个点\(m(m\leq10^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一样维护啦。不过linkcut时需要考虑到边的影响。

时间复杂度\(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\n",ans<INF?ans:-1);
    return 0;
}

P.S.

我这两天又有点咸了...

posted @ 2018-03-11 22:43  VisJiao  阅读(185)  评论(0编辑  收藏  举报