zoukankan      html  css  js  c++  java
  • 【洛谷P2387】魔法森林

    这个题就是先按照a从小到大排序,然后lct尽可能维护b值最小,在这个过程中寻找答案

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    
    int x,y,a,b;
    
    struct in
    {
        int f,l,a,b;
    }ter[100010];
    
    int son[200020][2],pre[200020],s1[200020],s[200020],fa[200020],lin[200020],n,m,ans = 1000000007;//lct并不是一棵实际存在的树(类似树链剖分,只不过现在是一棵splay),所以我们单独建树 
    //son存储splay的蛾子,pre存储splay的父亲(因为被分成一块块的树,就如树剖) 
    bool flag[200020];
    
    inline void re(int &x)
    {
        x = 0;
        bool fl = 0;
        char a = getchar();
        while(a < '0' || a > '9')
        {
            if(a == '-')
                fl = 1;
            a = getchar();
        }
        while(a >= '0' && a <= '9')
            x = x * 10 + a - '0',a = getchar();
    }
    
    bool cmp(in a,in b)
    {
        return a.a < b.a;
    }
    
    int find(int x)//并查集日常操作 
    {
        return fa[x] == x ? x : fa[x] = find(fa[x]);
    }
    
    bool isroot(int x)//判断是不是根 
    {
        return son[pre[x]][0] != x && son[pre[x]][1] != x;
    }
    
    void spread(int x)//翻转 
    {
        if(!flag[x])
            return;
        swap(son[x][0],son[x][1]);//左右蛾子互换 
        flag[son[x][0]] ^= 1,flag[son[x][1]] ^= 1,flag[x] = 0;
    }
    
    void up(int x)//更新 
    {
        int ls = son[x][0],rs = son[x][1];
        s1[x] = x;
        if(s[s1[ls]] > s[s1[x]]) s1[x] = s1[ls];
        if(s[s1[rs]] > s[s1[x]]) s1[x] = s1[rs];
    }
    
    bool dir(int x)//判断蛾子与父亲的相对关系 
    {
        return son[pre[x]][1] == x;
    }
    
    void rotate(int x)//日常旋转 
    {
        int d = dir(x),f = pre[x],g = pre[f];
        if(!isroot(f))
            son[g][dir(f)] = x;
        son[f][d] = son[x][d ^ 1],pre[son[x][d ^ 1]] = f,son[x][d ^ 1] = f; 
        up(g),up(f),pre[x] = g,pre[f] = x;
    }
    
    void splay(int x)
    {
        int i;
        for(i = x;!isroot(i);i = pre[i])//作为栈记录一下需要清空标记的点 
            lin[++ lin[0]] = i;
        lin[++ lin[0]] = i;
        while(lin[0])//清空标记 
            spread(lin[lin[0] --]);
        while(!isroot(x))//正常旋转 
        {
            int y = pre[x],z = pre[y];
            if(!isroot(y))
            {
                if((son[pre[y]][1] == y) == (son[pre[z]][1] == z))
                    rotate(y);
                else
                    rotate(x);
            }
            rotate(x);
        }
        up(x);
    }
    
    void access(int x)//打通x到树根的路径 
    {
        int la = 0;
        while(x)//直到树根 
        {
            splay(x);//把x转到子树的树根 
            son[x][1] = la,la = x,x = pre[x];//然后把x的父亲(在上层子树上)进行旋转 
        }
    }
    
    void makeroot(int x)//先让x和树根在同一个splay,然后再旋转到树根,打上标记(因为换根操作中只有蛾子与父亲的相对关系发生改变) 
    {
        access(x),splay(x),flag[x] ^= 1; 
    }
    
    void ask(int x,int y)//询问树上信息的时候,先把x转到树根,再使得y连接到树根 
    {
        makeroot(x),access(y),splay(y);
    } 
    
    void link(int x,int y)//先把x旋转到树根,然后把y作为x的父亲,这样就链接了 
    {
        makeroot(x),pre[x] = y;
    }
    
    void cut(int x,int y)//切断的时候要先把x作为y的左蛾子(因为ask函数使得这两者相连),然后再清除一下pre,son数组 
    {
        ask(x,y),pre[x] = son[y][0] = 0;
    }
    
    int main()
    {
        re(n),re(m);
        for(int i = 1;i <= m;i ++)
            re(ter[i].f),re(ter[i].l),re(ter[i].a),re(ter[i].b);
        sort(ter + 1,ter + 1 + m,cmp);//先保证a种精灵是按照最小生成树维护 
        for(int i = 1;i <= n;i ++)
            fa[i] = i;
        for(int i = 1;i <= m;i ++)
        {
            s[i + n] = ter[i].b; 
            int fx = find(ter[i].f),fy = find(ter[i].l);
            if(fx != fy)//如果这两个点不相连,直接将两者合并到一个集合 
                link(i + n,ter[i].f),link(i + n,ter[i].l),fa[fx] = fy;
            else
            {
                ask(ter[i].f,ter[i].l);//否则加进去说明出现了环,那么找环上b最大的边替换掉 
                int no = s1[ter[i].l];//我们用虚点来作为两个点的中转站 
                if(s[i + n] >= s[no])//如果现在要加的边的b值比环上b最大的边还要大的,就没有必要替换了 
                    continue;
                cut(no,ter[no - n].f),cut(no,ter[no - n].l);//我们把原来那条边拆掉 
                link(i + n,ter[i].f),link(i + n,ter[i].l);//再用新的边替换 
            }
            int ff = find(1),fg = find(n);
            if(ff == fg)//如果发现现在起点到终点可以联通,试图更新答案(因为a变大了) 
                ask(1,n),ans = min(ans,ter[i].a + s[s1[n]]);
            
        }
        if(ans >= 1000000007)
            printf("-1");
        else
            printf("%d",ans);
    }
  • 相关阅读:
    Ubuntu下安装 jdk6
    开发内容分解的9个角度
    2020年,为什么我们应该使用abapGit代替SAPLink
    如何禁用IntelliJ IDEA的LightEdit模式
    通过AMDP调用HANA的PAL函数
    用Python开发的Wox股票插件
    概念验证:在Kubernetes中部署ABAP
    使用ABAP Data Validator验证数据有效性
    开源工具abaplint的介绍
    【被面试官吊打】从系统角度考虑性能优化
  • 原文地址:https://www.cnblogs.com/Loi-dfkdsmbd/p/8449672.html
Copyright © 2011-2022 走看看