• BZOJ3669


    Portal

    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一样维护啦。不过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
    ",ans<INF?ans:-1);
        return 0;
    }
    

    P.S.

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

  • 相关阅读:
    【Codeforces 933A】A Twisty Movement
    【Codeforces 996B】World Cup
    【Codeforces 469B】Chat Online
    鼠标点击后的CSS3跑马灯效果
    CSS的相对定位和绝对定位
    CSS3制作的一款按钮特效
    单元测试小结
    JS对输入判断变化屏蔽中文输入法输入时连续触发事件的方法
    jquery的load方法
    MVC视图特性
  • 原文地址:https://www.cnblogs.com/VisJiao/p/BZOJ3669.html
Copyright © 2020-2023  润新知