zoukankan      html  css  js  c++  java
  • 洛谷P4234 最小差值生成树(LCT,生成树)

    洛谷题目传送门

    和魔法森林有点像,都是动态维护最小生成树(可参考一下Blog的LCT总结相关部分)

    至于从小到大还是从大到小当然无所谓啦,我是从小到大排序,每次枚举边,还没连通就连,已连通就替换环上最小的一条边,可以保证最优。如果已经构成了生成树,就可以更新答案,因为当前枚举到的一定是生成树里最大的,所以直接用当前减去最小更新答案。

    至于最小的怎样维护,其实根本不需要什么别的set什么的数据结构。只要标记一下在生成树中的边,再搞一个指针指向在树中最小的边就好啦。当最小的边也被替换,就把指针后移,直到再找到一个在树中的边为止。

    吐槽:注意了,有自环!!!我本来该1A却调试了2h,本地拿管理员的标程自造数据(没造自环)对拍几十万组无问题?!

    卡常的地方挺多的,在LCT中应该算挺快的吧(比下面Niko巨佬的LCT代码快了(1 over 3)左右,但是Niko巨佬写了个rank1的代码?!仔细看了下,是优化的暴力?貌似会被卡成(O(NM))?!强烈建议再加强数据。。。。。。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define R register int
    #define I inline void
    #define lc c[x][0]
    #define rc c[x][1]
    #define in(z) ini=&z;
        while(*++q<'-');
        *ini=*q&15;
        while(*++q>'-')*ini*=10,*ini+=*q&15//读入卡常
    const int N=50001,M=200009,L=N+M;
    int f[L],c[L][2],mn[L],ff[N];
    unsigned short v[L];//short卡常
    bool r[L],vis[M];
    char str[M<<6];
    struct EDGE{
        int u,v,l;
        inline bool operator<(EDGE x)const{
            return l<x.l;
        }
    }e[M];
    inline bool nroot(R x){return c[f[x]][0]==x||c[f[x]][1]==x;}
    inline int get(R x,R y){return v[x]<v[y]?x:y;}
    I pushup(R x){mn[x]=get(x,get(mn[lc],mn[rc]));}
    I pushdown(R x){
        if(r[x]){
            R t=lc;
            r[lc=rc]^=1;r[rc=t]^=1;r[x]=0;
        }
    }
    I pushall(R x){
        if(nroot(x))pushall(f[x]);
        pushdown(x);
    }
    I rotate(R x){
        R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
        if(nroot(y))c[z][c[z][1]==y]=x;c[x][!k]=y;c[y][k]=w;
        f[w]=y;f[y]=x;f[x]=z;
        pushup(y);
    }
    I splay(R x){
        R y=x;
        pushall(x);
        while(nroot(x)){
            if(nroot(y=f[x]))
                rotate((c[y][0]==x)^(c[f[y]][0]==y)?x:y);
            rotate(x);
        }
        pushup(x);
    }
    I access(R x){
        for(R y=0;x;x=f[y=x])
            splay(x),rc=y,pushup(x);
    }
    I mroot(R x){
        access(x);splay(x);
        r[x]^=1;
    }
    I link(R i){//卡常版写法
        mroot(e[i].u);
        f[f[e[i].u]=N+i]=e[i].v;
    }
    I cut(R x){//也是卡常版写法
        access(e[x-N].u);splay(x);
        lc=rc=f[lc]=f[rc]=0;
    }
    int getf(R x){//并查集卡常
        if(x==ff[x])return x;
        return ff[x]=getf(ff[x]);
    }
    int main(){
        fread(str,1,sizeof(str),stdin);//fread卡常
        R n,m,i,x,y,h,cnt,ans,*ini;
        register char*q=str-1;
        in(n);in(m);
        for(i=0;i<=n;++i)
            ff[i]=i,v[i]=-1;
    //-1放在unsigned里等于是极大值,注意v[0]也改了
        for(i=1;i<=m;++i){
            in(e[i].u);in(e[i].v);in(e[i].l);
        }
        sort(e+1,e+m+1);
        for(cnt=h=i=1;i<=m;++i){
            v[i+N]=e[i].l;
            if(getf(x=e[i].u)!=getf(y=e[i].v))
            {
                vis[i]=1,link(i),ff[ff[x]]=ff[y],++cnt;
                if(cnt==n)ans=e[i].l-e[h].l;
    //刚完全建好生成树要马上更新答案
            }
            else{
                if(x==y)continue;
                vis[i]=1;
                mroot(x);
                access(y);splay(y);
                vis[mn[y]-N]=0;while(!vis[h])++h;//维护好最小边
                cut(mn[y]);link(i);
                if(cnt==n)ans=min(ans,e[i].l-e[h].l);
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    币圈惊现门罗币挖矿新家族「罗生门」
    5步告诉你QQ音乐的完美音质是怎么来的,播放器的秘密都在这里
    【云+社区极客说】新一代大数据技术:构建PB级云端数仓实践
    Android P的APP适配总结,让你快人一步
    C++11用于计算函数对象返回类型的统一方法
    C++11用于元编程的类别属性
    C++11多态函数对象包装器
    C++11包装引用
    C++11能用智能指针
    C++正则表达式
  • 原文地址:https://www.cnblogs.com/flashhu/p/8576460.html
Copyright © 2011-2022 走看看