zoukankan      html  css  js  c++  java
  • NOIP2013 D1T3 货车运输 倍增LCA OR 并查集按秩合并

    思路:
    Kruskal求最大生成树+倍增LCA

    // by SiriusRen
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define N 105000
    int n,m,tot=0,xx,yy,zz,ans;
    int first[N],v[N*10],next[N*10],w[N*10],f[N],dep[N],fa[N][20],minn[N][20];
    int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
    struct EDGE{int from,to,weight;}Edge[50500];
    void add(int x,int y,int z){
        w[tot]=z,v[tot]=y;
        next[tot]=first[x];
        first[x]=tot++;
    }
    bool cmp(EDGE x,EDGE y){return x.weight>y.weight;}
    void dfs(int x){
        for(int j=1;j<=18;j++){
            fa[x][j]=fa[fa[x][j-1]][j-1];
            minn[x][j]=min(minn[x][j-1],minn[fa[x][j-1]][j-1]);
        }
        for(int i=first[x];~i;i=next[i])
            if(dep[v[i]]==-1){
                dep[v[i]]=dep[x]+1;
                fa[v[i]][0]=x;minn[v[i]][0]=w[i];
                dfs(v[i]);
            }
    }
    int lca(int x,int y){
        int ans=0x3fffffff;
        if(dep[x]<dep[y])swap(x,y);
        for(int i=18;i>=0;i--)if(dep[x]>=dep[y]+(1<<i))ans=min(ans,minn[x][i]),x=fa[x][i];
        if(x==y)return ans;
        for(int i=18;i>=0;i--)
            if(fa[x][i]!=fa[y][i]){
                ans=min(ans,min(minn[x][i],minn[y][i]));
                x=fa[x][i];y=fa[y][i];
            }
        return min(ans,min(minn[x][0],minn[y][0]));
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)f[i]=i; 
        memset(dep,-1,sizeof(dep));
        memset(minn,0x3f,sizeof(minn));
        memset(first,-1,sizeof(first));
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&xx,&yy,&zz);
            Edge[i].from=xx;Edge[i].to=yy;Edge[i].weight=zz;
        }
        sort(Edge+1,Edge+1+m,cmp);
        for(int i=1;i<=m;i++)
            if(find(Edge[i].from)!=find(Edge[i].to)){
                f[find(Edge[i].from)]=find(Edge[i].to);
                add(Edge[i].from,Edge[i].to,Edge[i].weight);
                add(Edge[i].to,Edge[i].from,Edge[i].weight);
            }
        dep[find(1)]=0;dfs(find(1));
        scanf("%d",&m);
        while(m--){
            scanf("%d%d",&xx,&yy);
            if(~dep[xx]&&~dep[yy])printf("%d
    ",lca(xx,yy));
            else puts("-1");
        }
    }

    这里写图片描述

    队长讲了还有一中很奇怪的方法可以乱搞。
    就是:Bling 并查集!
    我们可以想到Kruskal进行的过程中是把两个连通块连起来,中间连的边一定比连通块里面的边要小。
    那么我们可以考虑按秩合并。。可以证明这样树的高度是log的。
    然后直接暴力求LCA即可
    网上是这么说的:

    启发式并查集,就是维护每个集合的深度,在合并两个集合的时候把小的那个集合挂在大集合下。
    在此题中呢,求最大生成树的同时,不把新加入的一条边作为计算答案的树,而是把两个集合的祖先加入树中,边权就是原来边的两个边权。看到这,不禁产生了疑问,树的边权和形态与求出的最大生成树都不一样,为啥能做???其实没有关系,因为新加入的边不影响
    原来集合中两点的答案,合并的两个集合中的点合并后肯定要经过原来这条边,那我把祖先接起来用原来边的边权也是一样的。
    但是这么做,由于使用了启发式合并,那么最后新的树高度可以证明不会超过logn(其实我也不会证大笑),那么我们不用倍增处理这棵树,直接暴力求lca即可,不仅代码短,而且常数小!!!

    // by SiriusRen
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define N 105000
    int n,m,tot=0,xx,yy,zz;
    int first[N],v[N*10],next[N*10],w[N*10],f[N],dep[N],fa[N],size[N],minn[N];
    int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
    struct EDGE{int from,to,weight;}Edge[50500];
    void add(int x,int y,int z){w[tot]=z,v[tot]=y;next[tot]=first[x];first[x]=tot++;}
    bool cmp(EDGE x,EDGE y){return x.weight>y.weight;}
    void dfs(int x){
        for(int i=first[x];~i;i=next[i])
            if(dep[v[i]]==-1){
                dep[v[i]]=dep[x]+1;
                fa[v[i]]=x;minn[v[i]]=w[i];
                dfs(v[i]);
            }
    }
    void lca(int x,int y){
        int ans=0x3fffffff;
        if(dep[x]>dep[y])swap(x,y);
        while(dep[x]!=dep[y])ans=min(minn[y],ans),y=fa[y];
        while(x!=y){
            ans=min(ans,min(minn[x],minn[y]));
            x=fa[x];y=fa[y];
        }
        printf("%d
    ",ans);
        return;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)size[i]=1;
        for(int i=1;i<=n;i++)f[i]=i; 
        memset(dep,-1,sizeof(dep));
        memset(first,-1,sizeof(first));
        memset(minn,0x3f,sizeof(minn));
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&xx,&yy,&zz);
            Edge[i].from=xx;Edge[i].to=yy;Edge[i].weight=zz;
        }
        sort(Edge+1,Edge+1+m,cmp);
        for(int i=1;i<=m;i++){
            int fx=find(Edge[i].from),fy=find(Edge[i].to);
            if(fx!=fy){
                if(size[fx]>size[fy])swap(fx,fy);
                f[fx]=fy;size[fy]+=fx;
                add(fx,fy,Edge[i].weight);add(fy,fx,Edge[i].weight);
            }
        }
        dep[find(1)]=0;dfs(find(1));
        scanf("%d",&m);
        while(m--){
            scanf("%d%d",&xx,&yy);
            if(~dep[xx]&&~dep[yy])lca(xx,yy);
            else puts("-1");
        }
    }
  • 相关阅读:
    C++虚函数表解析(转)
    学习网址
    css 段落文字换行问题
    移动端fixed兼容问题
    半数集1
    汇编寄存器
    设计模式概述
    Vector用法介绍
    汇编PC硬件基本特征
    android 反编译总结
  • 原文地址:https://www.cnblogs.com/SiriusRen/p/6532409.html
Copyright © 2011-2022 走看看