zoukankan      html  css  js  c++  java
  • 【CodeForces】827 D. Best Edge Weight 最小生成树+倍增LCA+并查集

    【题目】D. Best Edge Weight

    【题意】给定n个点m条边的带边权无向连通图,对每条边求最大边权,满足其他边权不变的前提下图的任意最小生成树都经过它。n,m<=2*10^5,1<=wi<=10^9。

    【算法】最小生成树+倍增LCA+并查集

    【题解】首先求出图的一个最小生成树M,则所有边分成树边和非树边。

    一、对于非树边(u,v),假设u和v在最小生成树M上的路径的最大边权是Max。要保证这条边在最小生成树上,只要w(u,v)=Max-1。

    下面证明w(u,v)=Max-1时,一定在任意最小生成树上。

    证明:假设另一个最小生成树OM不包含(u,v),那么u和v在最小生成树OM上的路径的所有边权<=Max-1,按照kruscal算法从小到大加边的情况,(u,v)一定会被最小生成树OM首先连通,故M不是最小生成树,矛盾。

    二、对于树边(u,v),假设所有在最小生成树M上的路径经过它的非树边的最小边权是Min。要保证这条边在最小生成树M上(不会被替换),只要w(u,v)=Min-1。

    证明:如果(u,v)已经是所有它所在的环中的最小边,那么一定会先被连通。

    最后,我们需要解决问题是:找到一个最小生成树,对于每条非树边找到路径最大值,然后给路径贡献最小值标记,最后统计树边的答案。

    这用树链剖分+线段树是很容易实现的,还可以用线段树合并(权值),不过最简便的是倍增+并查集。

    倍增:记录路径最大值,即可回答第一个询问。

    并查集:非树边从小到大排序后依次处理,标记到的边就是最小值了,处理完后用并查集并起来以后不再处理(初始fa[i]=i),即每个点的父亲指向祖先中最近的未处理点(边),类似安全路经Travel

    注意先kruscal后按照生成树边来dfs建树。答案可能有0。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=200010;
    struct edge{int id,u,v,w,from;}e[maxn*2],ed[maxn*2];
    int n,m,first[maxn],tot,f[maxn][21],g[maxn][21],deep[maxn],fa[maxn],a[maxn],E[maxn],ans[maxn];
    void insert(int u,int v,int w,int id){tot++;e[tot].id=id;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
    bool cmp(edge a,edge b){return a.w<b.w||(a.w==b.w&&a.id<b.id);}
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    void dfs(int x,int fa){
        for(int i=1;(1<<i)<=deep[x];i++){
            f[x][i]=f[f[x][i-1]][i-1];
            g[x][i]=max(g[x][i-1],g[f[x][i-1]][i-1]);
        }
        for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa&&a[e[i].id]){
            deep[e[i].v]=deep[x]+1;
            f[e[i].v][0]=x;
            g[e[i].v][0]=e[i].w;
            E[e[i].v]=e[i].id;
            dfs(e[i].v,x);
        }
    }
    int lca(int x,int y){
        if(deep[x]<deep[y])swap(x,y);
        int ans=0,d=deep[x]-deep[y];
        for(int i=0;i<=20;i++)if(d&(1<<i))ans=max(ans,g[x][i]),x=f[x][i];
        if(x==y)return ans;
        for(int i=20;i>=0;i--)if((1<<i)<=deep[x]&&f[x][i]!=f[y][i]){
            ans=max(ans,max(g[x][i],g[y][i]));
            x=f[x][i];y=f[y][i];
        }
        return max(ans,max(g[x][0],g[y][0]));
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&ed[i].u,&ed[i].v,&ed[i].w);//
            ed[i].id=i;
        }
        sort(ed+1,ed+m+1,cmp);
        for(int i=1;i<=n;i++)fa[i]=i;
        for(int i=1;i<=m;i++){
            int x=find(ed[i].u),y=find(ed[i].v);
            if(x!=y){a[ed[i].id]=1;fa[x]=y;}
        }
        for(int i=1;i<=m;i++)insert(ed[i].u,ed[i].v,ed[i].w,ed[i].id),insert(ed[i].v,ed[i].u,ed[i].w,ed[i].id);
        dfs(1,0);
        for(int i=1;i<=n;i++)fa[i]=i;
        memset(ans,-1,sizeof(ans));//
        for(int i=1;i<=m;i++)if(!a[ed[i].id]){
            int x=find(ed[i].u),y=find(ed[i].v);
            ans[ed[i].id]=lca(ed[i].u,ed[i].v)-1;
            while(x!=y){
                if(deep[x]<deep[y])swap(x,y);
                ans[E[x]]=ed[i].w-1;
                x=fa[x]=find(f[x][0]);//
            }
        }
        for(int i=1;i<=m;i++)printf("%d ",ans[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    97.5%准确率的深度学习中文分词(字嵌入+Bi-LSTM+CRF)
    TensorFlow教程——Bi-LSTM+CRF进行序列标注(代码浅析)
    对Attention is all you need 的理解
    SpringBoot入门最详细教程
    TensorFlow saved_model 模块
    hadoop无法启动
    信号
    rpcserver不可用
    4444: [Scoi2015]国旗计划|贪心|倍增
    最小公倍数 SRM 661 Div1 250: MissingLCM
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8525639.html
Copyright © 2011-2022 走看看