zoukankan      html  css  js  c++  java
  • Codeforces 609E (Kruskal求最小生成树+树上倍增求LCA)

    题面

    传送门
    题目大意:
    给定一个无向连通带权图G,对于每条边(u,v,w),求包含这条边的生成树大小的最小值

    分析

    包含这条边的生成树的大小如何表示呢?
    先求出整张图的最小生成树大小tlen,对于每一条边(u,v,w),我们最小生成树中去掉树上从u到v的路径上权值最大,最大值为mlen的一条边,再加上w,得到的一定是包含这条边的生成树大小的最小值tlenmlen+w

    最小生成树大小tlen可用kruskal算法在O(mlog2m)时间内求出
    那么问题转化为求mlen,可用树上倍增法求解

    树上倍增法的好处是在求LCA的同时可以维护更多的附加信息
    在求LCA的过程中设fa[i][j]表示i的2j辈祖先
    可写出公式

    fa[i][j]=fa[fa[i][j1]][j1]

    (即i的2j辈祖先是i的2j1辈祖先的2j1辈祖先)
    同理可写出最大长度
    mlen[i][j]=max(mlen[i][j1],mlen[mlen[i][j1]][j1])

    查询时类似LCA的查询即可,详情见代码

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<cmath>
    #define maxn 200005
    #define maxm 200005
    #define maxlog 32
    using namespace std;
    int n,m;
    inline int qread(){
        int x=0,sign=1;
        char c=getchar();
        while(c<'0'||c>'9'){
            if(c=='-') sign=-1;
            c=getchar();
        }
        while(c>='0'&&c<='9'){
            x=x*10+c-'0';
            c=getchar();
        }
        return x*sign;
    }
    struct edge{
        int from;
        int to;
        int len;
        int next;
        int index;
        edge(){
    
        }
        edge(int x,int y,int z,int i){
            from=x;
            to=y;
            len=z;
            index=i;
        }
        friend bool operator <(edge x,edge y){
            return x.len<y.len;
        }
    };
    edge G[maxm*2],MST[maxm*2];
    int head[maxn];
    int size=0;
    void add_edge(int u,int v,int w){
        size++;
        MST[size].from=u;
        MST[size].to=v;
        MST[size].len=w;
        MST[size].next=head[u];
        head[u]=size;
    }
    int fset[maxn];
    void set_init(){
        for(int i=1;i<=n;i++) fset[i]=i;
    }
    int find(int x){
        if(fset[x]==x) return x;
        else{
            fset[x]=find(fset[x]);
            return fset[x];
        }
    }
    long long kruskal(){
        long long ans=0;
        sort(G+1,G+1+m);
        for(int i=1;i<=m;i++){
            int fx=find(G[i].from);
            int fy=find(G[i].to);
            if(fx!=fy){
                add_edge(G[i].from,G[i].to,G[i].len);
                add_edge(G[i].to,G[i].from,G[i].len);
                fset[fx]=fy;
                ans+=G[i].len; 
            }
        }
        return ans;
    }
    
    int deep[maxn],fa[maxn][maxlog];
    long long mlen[maxn][maxlog];
    int log2n;
    void lca_init(){
        queue<int>q;
        q.push(1);
        deep[1]=1; //初始化深度
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=head[x];i;i=MST[i].next){//MST表示最小生成树的边
                int y=MST[i].to;
                if(deep[y]) continue;
                deep[y]=deep[x]+1;
                fa[y][0]=x;//fa和mlen的初始值
                mlen[y][0]=MST[i].len;
                for(int j=1;j<=log2n;j++){//倍增初始化
                    fa[y][j]=fa[fa[y][j-1]][j-1];
                    mlen[y][j]=max(mlen[y][j-1],mlen[fa[y][j-1]][j-1]);
                }
                q.push(y);
            }
        }
    }
    long long lca_query(int x,int y){
        if(deep[x]>deep[y]) swap(x,y);
        long long maxl=0;
        for(int i=log2n;i>=0;i--){//先将x和y调整到同一深度
            if(deep[fa[y][i]]>=deep[x]){
                maxl=max(maxl,mlen[y][i]);//y上升同时更新maxl
                y=fa[y][i];
            }
        }
        if(x==y) return maxl;//如果LCA(x,y)=x,直接返回
        for(int i=log2n;i>=0;i--){//x,y同时上升,直到差一条边相遇
            if(fa[x][i]!=fa[y][i]){
                maxl=max(maxl,max(mlen[x][i],mlen[y][i]));
                x=fa[x][i];
                y=fa[y][i];
            }
        }
        maxl=max(maxl,max(mlen[x][0],mlen[y][0]));//最后再更新一次
        return maxl;
    }
    long long ans[maxm];//便于按输入顺序输出
    int main(){
        int s,t,r;
        n=qread();
        m=qread();
        for(int i=1;i<=m;i++){
            s=qread();
            t=qread();
            r=qread();
            G[i]=edge(s,t,r,i);
        }
        set_init();
        long long tlen=kruskal();
        log2n=log2(n)+1;
        lca_init();
        for(int i=1;i<=m;i++){
            ans[G[i].index]=tlen+(long long)G[i].len-(long long)lca_query(G[i].from,G[i].to);//求生成树大小的最小值
        }
        for(int i=1;i<=m;i++){
            printf("%I64d
    ",ans[i]);
        }
    }
  • 相关阅读:
    Android Media Playback 中的MediaPlayer的用法及注意事项(二)
    Android Media Playback 中的MediaPlayer的用法及注意事项(一)
    34. Search for a Range
    33. Search in Rotated Sorted Array
    32. Longest Valid Parentheses
    31. Next Permutation下一个排列
    30. Substring with Concatenation of All Words找出串联所有词的子串
    29. Divide Two Integers
    28. Implement strStr()子串匹配
    27. Remove Element
  • 原文地址:https://www.cnblogs.com/birchtree/p/9845832.html
Copyright © 2011-2022 走看看