zoukankan      html  css  js  c++  java
  • bzoj 3669 [Noi2014]魔法森林

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3669

    第二道LCT!

    直接看TJ……https://blog.csdn.net/clove_unique/article/details/51317842

    把连边表示成与点相连 是很好的。因为splay中的边变动得挺随意的。

    感觉理解又加深了。那个query的部分,效果是x和y所在的辅助树中x深度最小、y深度最大,即当前辅助树就是x和y之间的链!

    因为忘记在splay的最后pshp,RE(为什么是RE?)了好久。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=5e4+5,M=1e5+5,INF=0x7fffffff;
    int n,m,pre[N+M],mx[N+M],c[N+M][2],fa[N],stack[N+M],top,val[N+M],ans;//val    //
    bool rev[N+M];
    struct Ed{
        int x,y,a,b;
        bool operator<(const Ed &k)const
            {return a<k.a;}
    }ed[M];
    int find(int a){return fa[a]==a?a:fa[a]=find(fa[a]);}
    bool isroot(int x){return c[pre[x]][0]!=x&&c[pre[x]][1]!=x;}
    void pshp(int x)
    {
        mx[x]=x;
        if(val[mx[c[x][0]]]>val[mx[x]])mx[x]=mx[c[x][0]];
        if(val[mx[c[x][1]]]>val[mx[x]])mx[x]=mx[c[x][1]];
    }
    void reverse(int x)
    {
        if(rev[x]){
            rev[c[x][0]]^=1;rev[c[x][1]]^=1;
            swap(c[x][0],c[x][1]);
            rev[x]=0;
        }
    }
    void rotate(int x)
    {
        int y=pre[x],z=pre[y],d=(x==c[y][1]);
        if(!isroot(y))c[z][y==c[z][1]]=x;
        pre[x]=z;pre[y]=x;
        c[y][d]=c[x][!d];pre[c[x][!d]]=y;c[x][!d]=y;
        pshp(y);pshp(x);
    }
    void splay(int x)
    {
        stack[top=1]=x;
        for(int t=x;!isroot(t);t=pre[t])stack[++top]=pre[t];
        for(;top;top--)reverse(stack[top]);
        for(;!isroot(x);rotate(x))
        {
            int y=pre[x],z=pre[y];
            if(isroot(y))continue;
            ((c[y][0]==x)^(c[z][0]==y))?rotate(x):rotate(y);
        }
        pshp(x);//////////
    }
    void access(int x)
    {
        for(int t=0;x;c[x][1]=t,t=x,x=pre[x])splay(x);
    }
    void makeroot(int x)
    {
        access(x);splay(x);rev[x]^=1;
    }
    int query(int x,int y)
    {
        makeroot(x);access(y);splay(y);//y没有右儿子     //access之后相当于在辅助树中splay,故不用rev 
    //    printf("x=%d y=%d c[x][0]=%d c[x][1]=%d c[y][0]=%d c[y][1]=%d
    ",x,y,c[x][0],c[x][1],c[y][0],c[y][1]);
        return mx[y];    //x和y之间的不应该是x的右儿子?
    //    int vmx=mx[c[x][1]];    //此时应该是y为根、无右儿子;x为y的左儿子,无左儿子。故。 
    //    if(val[vmx]<val[x])vmx=x;    //但为什么这样写不行? 
    //    if(val[vmx]<val[y])vmx=y;    //y的左儿子可能不是x。在access的splay中,x可能在自己的辅助树中被转走 
    //    return vmx;    //但makert(x)使x变成深度最小的,access(y)使y变成深度最大的,故此时辅助树中只有链上的。 
    }
    void link(int x,int y)
    {
        makeroot(x);pre[x]=y;//此时不用pshp--query的时候会弄好 
    }
    void cut(int x,int y)
    {
        query(x,y);
        pre[x]=0;c[y][0]=0;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d%d%d",&ed[i].x,&ed[i].y,&ed[i].a,&ed[i].b);
        sort(ed+1,ed+m+1);
        for(int i=1;i<=n;i++)fa[i]=i;
        ans=INF;
        for(int i=1;i<=m;i++)
        {
            int u=ed[i].x,v=ed[i].y;val[i+n]=ed[i].b;//val应记录排序后的 
            if(find(u)!=find(v)){
                fa[find(u)]=find(v);
                link(i+n,u);link(i+n,v);
                if(find(1)==find(n))ans=min(ans,ed[i].a+val[query(1,n)]);
            }
            else{
                query(u,v);int k=mx[v];if(val[k]<val[i+n])continue;//
                cut(k,ed[k-n].x);cut(k,ed[k-n].y);
                link(i+n,u);link(i+n,v);
                if(find(1)==find(n))ans=min(ans,ed[i].a+val[query(1,n)]);    //这里+val[query(1,n)],不是val[i+n] 
            }
        }
        printf("%d",(ans==INF?-1:ans));
        return 0;
    }
  • 相关阅读:
    使用Redis的理由
    从输入网址到显示网页的全过程分析
    Node.js初识
    GET和POST的数据传递到底有何区别?
    第四五六周学习进度
    首尾相接整数数组中最大子数组的和
    网页版四则运算
    团队介绍及项目简介
    整数数组中最大子数组的和
    软件工程个人作业03
  • 原文地址:https://www.cnblogs.com/Narh/p/9234470.html
Copyright © 2011-2022 走看看