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]);
        }
    }
  • 相关阅读:
    Jdk 1.6 在线 API 中文版
    数据库的最简单实现
    互联网公司GitHub repo 语言使用情况
    Chrome浏览器查看 iframe信息 OpenFrame
    PostgreSQL 保存json,jsonb类型
    修改PS1变量
    postgres json
    PostgreSQL PL/Python 和 PL/Postgres 函数互相调用
    转:CentOS 6.x 挂载读写NTFS分区(fuse-ntfs-3g)
    CentOS 7 设置静态IP
  • 原文地址:https://www.cnblogs.com/birchtree/p/9845832.html
Copyright © 2011-2022 走看看