zoukankan      html  css  js  c++  java
  • BZOJ3669[Noi2014]魔法森林——kruskal+LCT

    题目描述

    为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。

    魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。

    只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。

    由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。

    输入

    第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。

    输出

    输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。

    样例输入

    【输入样例1】
    4 5
    1 2 19 1
    2 3 8 12
    2 4 12 15
    1 3 17 8
    3 4 1 17
    【输入样例2】
    3 1
    1 2 1 1

    样例输出

    【输出样例1】
    32
    【样例说明1】
    如果小E走路径1→2→4,需要携带19+15=34个守护精灵;
    如果小E走路径1→3→4,需要携带17+17=34个守护精灵;
    如果小E走路径1→2→3→4,需要携带19+17=36个守护精灵;
    如果小E走路径1→3→2→4,需要携带17+15=32个守护精灵。
    综上所述,小E最少需要携带32个守护精灵。
    【输出样例2】

    -1
    【样例说明2】
    小E无法从1号节点到达3号节点,故输出-1。

    提示

    2<=n<=50,000

    0<=m<=100,000

    1<=ai ,bi<=50,000

    求一个二维的最小生成树(最小生成森林)中的最大边权,一维的很好做直接按边权排序后加边即可。

    那么二维的怎么搞?

    我们依旧按第一维排序,这样保证了第一维一定是最小生成树,按排序后顺序枚举所有边,当边两端的点不连通就加入这条边,直接用LCT,link一下即可。

    当边两端的点联通,那么我们判断当前枚举边的第二维和1到n路径上第二维边权最大值的关系,如果当前枚举的边小就把那条边删除,加上这条边。

    splay要维护第二维边权的最大值,但在LCT上维护边信息并不好做,因此我们将新建一个点tmp来代表边,它的点权就是它代表边的边权。

    例如x,y之间有一条边,那么在LCT中就把(x,tmp)和(y,tmp)连接起来。

    每次枚举一条边之后判断1和n是否联通,只要联通不管当前边是否被选都用当前边第一维边权和1到n路径上第二维边权最大值来更新答案。

    这样更新为什么是对的?

    假如这条边选了并且在1到n的路径上,那么这么更新是可以的。

    假如这条边选了但不在1到n的路径上,那么说明在这条边选之前1与n就是联通的,这次更新的值一定比答案大,不影响。

    假如这条边没选,那这次更新的值也一定比当前答案大,一定不会影响答案。

    #include<set>
    #include<map>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<vector>
    #include<cstdio>
    #include<bitset>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int n,m;
    int ans;
    int r[150010];
    int v[150010];
    int f[150010];
    int st[150010];
    int s[150010][2];
    int num[150010];
    struct miku
    {
        int x,y;
        int a,b;
    }g[1000010];
    bool cmp(miku p,miku q)
    {
        return p.a<q.a;
    }
    int get(int rt)
    {
        return rt==s[f[rt]][1];
    }
    int is_root(int rt)
    {
        return rt!=s[f[rt]][0]&&rt!=s[f[rt]][1];
    }
    void pushup(int rt)
    {
        num[rt]=rt;
        if(v[num[s[rt][0]]]>v[num[rt]])
        {
            num[rt]=num[s[rt][0]];
        }
        if(v[num[s[rt][1]]]>v[num[rt]])
        {
            num[rt]=num[s[rt][1]];
        }
    }
    void pushdown(int rt)
    {
        if(r[rt])
        {
            r[s[rt][0]]^=1;
            r[s[rt][1]]^=1;
            r[rt]^=1;
            swap(s[rt][0],s[rt][1]);
        }
    }
    void rotate(int rt)
    {
        int fa=f[rt];
        int anc=f[fa];
        int k=get(rt);
        if(!is_root(fa))
        {
            s[anc][get(fa)]=rt;
        }
        s[fa][k]=s[rt][k^1];
        f[s[fa][k]]=fa;
        s[rt][k^1]=fa;
        f[fa]=rt;
        f[rt]=anc;
        pushup(fa);
        pushup(rt);
    }
    void splay(int rt)
    {
        int top=0;
        st[++top]=rt;
        for(int i=rt;!is_root(i);i=f[i])
        {
            st[++top]=f[i];
        }
        for(int i=top;i>=1;i--)
        {
            pushdown(st[i]);
        }
        for(int fa;!is_root(rt);rotate(rt))
        {
            if(!is_root(fa=f[rt]))
            {
                rotate(get(fa)==get(rt)?fa:rt);
            }
        }
    }
    void access(int rt)
    {
        for(int x=0;rt;x=rt,rt=f[rt])
        {
            splay(rt);
            s[rt][1]=x;
            pushup(rt);
        }
    }
    void reverse(int rt)
    {
        access(rt);
        splay(rt);
        r[rt]^=1;
    }
    int find(int rt)
    {
        access(rt);
        splay(rt);
        while(s[rt][0])
        {
            rt=s[rt][0];
        }
        return rt;
    }
    void link(int x,int y)
    {
        reverse(x);
        f[x]=y;
    }
    void cut(int x,int y)
    {
        reverse(x);
        access(y);
        splay(y);
        f[x]=0;
        s[y][0]=0;
        pushup(y);
    }
    int main()
    {
        ans=1<<30;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            num[i]=i;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&g[i].x,&g[i].y,&g[i].a,&g[i].b);
        }
        sort(g+1,g+1+m,cmp);
        for(int i=1;i<=m;i++)
        {
            int x=g[i].x;
            int y=g[i].y;
            if(find(x)!=find(y))
            {
                v[n+i]=g[i].b;
                num[n+i]=n+i;
                link(x,n+i);
                link(y,n+i);
            }
            else
            {
                reverse(x);
                access(y);
                splay(y);
                int val=num[y];
                if(v[val]>g[i].b)
                {
                    cut(val,g[val-n].x);
                    cut(val,g[val-n].y);
                    num[n+i]=n+i;
                    v[n+i]=g[i].b;
                    link(n+i,x);
                    link(n+i,y);
                }
            }
            if(find(1)==find(n))
            {
                reverse(1);
                access(n);
                splay(n);
                ans=min(ans,g[i].a+v[num[n]]);
            }
        }
        if(ans==(1<<30))
        {
            printf("-1");
            return 0;
        }
        printf("%d",ans);
    }
  • 相关阅读:
    mybatis中的延迟加载
    MyBatis的mapper
    MyBatis的resultMap
    mybatis入门
    mybatis中的#和$的区别(转)
    操作日志记录
    SpringMVC中的异常处理集锦
    vue.js的package.json相关问题解惑
    git的常用操作指令
    http协议参数详解
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/9745446.html
Copyright © 2011-2022 走看看