zoukankan      html  css  js  c++  java
  • p4180 次小生成树

    传送门

    分析

    次小生成树的求法有两种,最大众的一种是通过倍增LCA找环中最大边求解,而这里我介绍一种神奇的O(nlogn) 做法:

        我们先建立最小生成树,因为我们用kruskal求解是边的大小已经按升序排列,所以相同情况下,先枚举的边一定更优,所以我们每一次暴力的找非树边所连两点的LCA,并在寻找过程中对经过的边染色同时将其加入并查集以防止其二次查询(为何只需查找一次之前已经说过),然后在最后,我们只需找出所染颜色所代表的边的权值减去被染色的边的权值的最小值即可。因为被染色的树边共有n-1条,所以此过程的复杂度是O(m),因此总复杂度即为快排复杂度O(mlogm)。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #include<stack>
    #include<map>
    #include<vector>
    #include<set>
    #include<ctime>
    #include<cctype>
    using namespace std;
    long long fa[110000],tot,used[310000],f[110000],col[310000],dep[110000];
    long long ari[110000],is[110000];
    long long sum=1;
    struct node{
        long long x,y,z;
    }d[310000];
    struct edge{
        long long from,to,nxt,w,id;
    }e[610000];
    long long head[610000];
    void add(long long x,long long y,long long z,long long id){
        e[sum].to=y;
        e[sum].nxt=head[x];
        e[sum].w=z;
        e[sum].id=id;
        head[x]=sum++;
        e[sum].to=x;
        e[sum].nxt=head[y];
        e[sum].w=z;
        e[sum].id=id;
        head[y]=sum++;
        return;
    }
    bool cmp(const node &p,const node &q){
        return p.z<q.z;
    }
    long long sf(long long a){
        return fa[a]==a?a:fa[a]=sf(fa[a]);
    }
    void dfs(long long a,long long fat){
        long long i,j,k;
        for(i=head[a];i;i=e[i].nxt)
           if(e[i].to!=fat){
            dep[e[i].to]=dep[a]+1;
            f[e[i].to]=a;
            ari[e[i].to]=e[i].id;
            dfs(e[i].to,a);
        }
        return;
    }
    long long ff(long long a){
        return is[a]==a?a:is[a]=ff(is[a]);
    }
    void mer(long long u,long long v,long long c){
        u=ff(u),v=ff(v);
        while(u!=v){
            if(dep[u]<dep[v])swap(u,v);
            col[ari[u]]=c;
            is[u]=ff(f[u]);
            u=ff(u);
        }
        return;
    }
    int main(){
        //freopen("1.in","r",stdin);
        long long n,m,i,j,k;
        scanf("%lld%lld",&n,&m);
        for(i=1;i<=m;i++){
           scanf("%lld%lld%lld",&d[i].x,&d[i].y,&d[i].z);
        }
        //建最小生成树
        sort(d+1,d+m+1,cmp);
        long long cnt=0,p,q;
        for(i=1;i<=n;i++){
           fa[i]=i;
           f[i]=i;
           is[i]=i;
        }
        for(i=1;i<=m;i++){
            p=sf(d[i].x),q=sf(d[i].y);
            if(p!=q){
                cnt++;
                if(rand()%2)fa[p]=q;
                  else fa[q]=p;        
                tot+=d[i].z;
                used[i]=1;
                add(d[i].x,d[i].y,d[i].z,i);
            }
            if(cnt==n-1)break;
        }
        //初始化,f表示父子关系,is用于新并查集
        dfs(1,0);
        for(i=1;i<=m;i++)
           if(!used[i]){
                mer(d[i].x,d[i].y,i);
           }
        //求答案
        long long ans=1000000007;
        for(i=1;i<=m;i++)
           if(used[i]){
                if(col[i]&&d[col[i]].z!=d[i].z)
                  ans=min(ans,d[col[i]].z-d[i].z);
           }
        printf("%lld ",ans+tot);
        return 0;
    }

  • 相关阅读:
    Servlet到底是单例还是多例你了解吗?
    Idea的一些调试技巧及设置todo
    Android如何使用so文件和Android studio中导入so
    Android 自定义控件之app标题栏的封装
    Android 动态添加删除ExpandableListView的item的例子
    Android 利用ListView制作带竖线的多彩表格
    Android EditText 改变边框颜色
    Android 使用Okhttp/Retrofit持久化cookie的简便方式
    Android 一个漂亮的Android Spinner
    Android 带清除功能的输入框控件EditText
  • 原文地址:https://www.cnblogs.com/yzxverygood/p/9016111.html
Copyright © 2011-2022 走看看