[NOI2014]魔法森林——动态加边最小MST

动态加点spfa点这里

题目大意:

给定一个图,每条边有两个权值,ai和bi,求一条路径使得这条路径上的边的amax+bmax
最小。

思路:

若只有一种权值,求出此图的MST后即可得到答案,考虑两种权值的情况,可以从小到大枚举一种权值,并按照第一种权值从小到大的顺序动态加边维护MST,用lct维护即可。
考虑如何将边权在lct中表示,我们可以将每一条边表示成一个点,连接这条边两边的端点即可,表示边的点的权值即为bi,表示点的点只起到连接作用,所以权值统一设成0.。
若新加的这条边连接不成环则直接连接,若成环,则删掉换上的大于此边的权值的最大的边,所以splay中维护的不仅有最大值,还有最大值的编号。

/*==============================
 * Author : Ylsoi
 * Problem : [NOI2014]magic
 * File : [NOI2014]magic_lct.cpp
 * Algorithm : MST,Link Cut Tree
 * Time : 2018.4.9
 * ============================*/
#include
#include
#include
#include
#include
#include
using namespace std;
void File(){
    freopen("[NOI2014]magic_lct.in","r",stdin);
    freopen("[NOI2014]magic_lct.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=5e4+10;
const int maxm=1e5+10;
int n,m,cnt;
struct edge{
    int u,v,a,b;
    bool operator < (const edge &tt) const {
        return aint Max(int _,int __){return _>__ ? _ : __;}
int Min(int _,int __){return _<__ ? _ : __;}
int dis[maxn],ans=inf;
bool vis[maxn];
struct lct{
#define lc ch[rt][0]
#define rc ch[rt][1]
#define rel(x) (ch[fa[x]][1]==x)
    int va[maxn+maxm],Maxva[maxn+maxm],Maxnum[maxn+maxm],fa[maxn+maxm],ch[maxn+maxm][2],q[maxn+maxm],top;
    bool tag[maxn+maxm];
    bool isrt(int rt){return ch[fa[rt]][0]!=rt && ch[fa[rt]][1]!=rt;}
    void pushup(int rt){
        Maxva[rt]=Max(va[rt],Max(Maxva[lc],Maxva[rc]));
        if(Maxva[rt]==va[rt])Maxnum[rt]=rt;
        else if(Maxva[rt]==Maxva[lc])Maxnum[rt]=Maxnum[lc];
        else Maxnum[rt]=Maxnum[rc];
    }
    void pushdown(int rt){if(tag[rt])tag[rt]^=1,tag[lc]^=1,tag[rc]^=1,swap(lc,rc);}
    void rotate(int rt){
        int f=fa[rt],r=rel(rt);
        fa[rt]=fa[f];if(!isrt(f))ch[fa[f]][rel(f)]=rt;
        fa[ch[rt][r^1]]=f;ch[f][r]=ch[rt][r^1];
        fa[f]=rt;ch[rt][r^1]=f;
        pushup(f);pushup(rt);
    }
    void splay(int rt){
        top=1;q[top]=rt;
        int u=rt;
        while(!isrt(u))u=fa[u],q[++top]=u;
        DREP(i,top,1)pushdown(q[i]);
        while(!isrt(rt)){
            int f=fa[rt];
            if(!isrt(f))rotate(rel(f)==rel(rt) ? f : rt);
            rotate(rt);
        }
    }
    void access(int rt){for(int las=0;rt;las=rt,rt=fa[rt])splay(rt),rc=las,pushup(rt);}
    void makert(int rt){access(rt);splay(rt);tag[rt]^=1;}
    int findrt(int rt){access(rt);splay(rt);while(lc)rt=lc;return rt;}
    bool con(int x,int y){makert(x);return (findrt(y)==x);}
    void split(int x,int y){makert(x);access(y);splay(y);}
    void cut(int x,int y){split(x,y);if(ch[y][0]==x)ch[y][0]=x,fa[x]=0,pushup(y);}
    void link(int x,int y){if(con(x,y))return;makert(x);fa[x]=y;}
    int query(int x,int y){if(!con(x,y))return inf;split(x,y);return Maxva[y];}
}T;
int main(){
    File();
    scanf("%d%d",&n,&m);
    REP(i,1,m){
        int u,v,a,b;
        scanf("%d%d%d%d",&u,&v,&a,&b);
        ++cnt;
        E[cnt].u=u;E[cnt].v=v;
        E[cnt].a=a;E[cnt].b=b;
    }
    sort(E+1,E+cnt+1);
    REP(i,1,n)T.va[i]=0,T.Maxnum[i]=i;
    REP(i,n+1,n+m)T.va[i]=E[i-n].b,T.Maxnum[i]=i;
    int p=1;
    while(p<=m){
        int i=p;
        while(E[i].a==E[p].a){
            int u=E[i].u,v=E[i].v;
            if(!T.con(u,v)){
                T.link(i+n,u);
                T.link(i+n,v);
            }
            else{
                T.split(u,v);
                int num=T.Maxnum[v];
                if(T.va[num]>E[i].b){
                    T.cut(E[num-n].u,num);
                    T.cut(E[num-n].v,num);
                    T.link(u,i+n);
                    T.link(v,i+n);
                }
            }
            ++i;
        }
        ans=Min(ans,T.query(1,n)+E[p].a);
        p=i;
    }
    if(ans!=inf)printf("%d\n",ans);
    else printf("-1\n");
    return 0;
}

你可能感兴趣的:(贪心,动态树,最小生成树)