1. 程式人生 > >BZOJ 3669: [Noi2014]魔法森林(lct+最小生成樹)

BZOJ 3669: [Noi2014]魔法森林(lct+最小生成樹)

傳送門

解題思路

  \(lct\)維護最小生成樹。我們首先按照\(a\)排序,然後每次加入一條邊,在圖中維護一棵最小生成樹。用並查集判斷一下\(1\)\(n\)是否聯通,如果聯通的話就嘗試更新答案。

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>

using namespace std;
const int MAXN = 50005;
const int MAXM = 1000005;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return f?x:-x;
}

int n,m,ans=1e9,F[MAXN],val[MAXN+MAXM],num;
int xx[MAXN+MAXM],yy[MAXN+MAXM];

struct Edge{
    int x,y,a,b;
    friend bool operator<(const Edge A,const Edge B){
        return A.a<B.a;
    }
}edge[MAXM];

int Find(int x){
    if(x==F[x]) return x;
    return F[x]=Find(F[x]);
}

namespace lct{
    int fa[MAXN+MAXM],ch[MAXN+MAXM][2],Max[MAXN+MAXM];
    bool tag[MAXN+MAXM];
    inline void pushup(int x){
        Max[x]=x;
        if(val[Max[ch[x][0]]]>val[Max[x]]) Max[x]=Max[ch[x][0]];
        if(val[Max[ch[x][1]]]>val[Max[x]]) Max[x]=Max[ch[x][1]];
    }
    inline void pushdown(int x){
        if(tag[x]){
            tag[x]^=1;swap(ch[x][0],ch[x][1]);
            if(ch[x][0]) tag[ch[x][0]]^=1;
            if(ch[x][1]) tag[ch[x][1]]^=1;
        }
    }
    inline bool isroot(int x){
        return (x!=ch[fa[x]][0] && x!=ch[fa[x]][1]);
    }
    inline bool check(int x){
        return (x==ch[fa[x]][1]);
    }
    void pd(int x){
        if(!isroot(x)) pd(fa[x]);pushdown(x);
    }
    inline void rotate(int x){
        int y=fa[x],z=fa[y];bool chk=check(x);
        if(!isroot(y)) ch[z][check(y)]=x;
        ch[y][chk]=ch[x][chk^1];fa[ch[x][chk^1]]=y;
        ch[x][chk^1]=y;fa[y]=x;fa[x]=z;pushup(y);pushup(x);
    }
    inline void splay(int x){
        pd(x);
        for(;!isroot(x);rotate(x))
            if(!isroot(fa[x])) rotate(check(fa[x])==check(x)?fa[x]:x);
    }
    inline void access(int x){
        for(int y=0;x;y=x,x=fa[x])
            splay(x),ch[x][1]=y,pushup(x);
    }
    inline void makeroot(int x){
        access(x);splay(x);tag[x]^=1;
    }
    inline void link(int x,int y){
        makeroot(x);fa[x]=y;pushup(y);
    }
    inline void split(int x,int y){
        makeroot(x);access(y);splay(x);
    }
    inline void cut(int x,int y){
        makeroot(x);access(y);splay(y);
        fa[x]=ch[y][0]=0;pushup(y);
    }
}

int main(){
    n=rd(),m=rd();int x,y,a,b,A,u,v;
    for(int i=1;i<=n;i++) F[i]=i;num=n;
    for(int i=1;i<=m;i++){
        x=rd(),y=rd(),a=rd(),b=rd();
        edge[i].x=x,edge[i].y=y,edge[i].a=a,edge[i].b=b;
    }
    sort(edge+1,edge+1+m);
    for(int i=1;i<=m;i++){
        A=edge[i].a;x=edge[i].x,y=edge[i].y;
        u=Find(edge[i].x);v=Find(edge[i].y);
        if(u!=v){
            val[++num]=edge[i].b;lct::link(x,num);lct::link(num,y);
            xx[num]=x;yy[num]=y;F[u]=v;
        } 
        else {
            lct::split(x,y);
            if(val[lct::Max[x]]>edge[i].b) {
                a=lct::Max[x];b=yy[lct::Max[x]];val[++num]=edge[i].b;
                lct::cut(xx[lct::Max[x]],lct::Max[x]);lct::cut(a,b);
                lct::link(x,num),lct::link(num,y);xx[num]=x;yy[num]=y;
            }
        } 
        if(Find(1)==Find(n)){
            lct::split(1,n);
            ans=min(ans,A+val[lct::Max[1]]);
        }
    }
    if(ans!=1e9) printf("%d\n",ans);
    else puts("-1");
    return 0;
}