zoukankan      html  css  js  c++  java
  • 【BJOI2010】次小生成树-最小生成树+倍增LCA

    题目大意:给定一个带边权的无向图,求该图中严格次小生成树上的边权和。
    做法:本题需要用到最小生成树+倍增LCA。
    所谓严格次小生成树,就是边权和严格大于最小生成树的边权和最小的生成树。我们求非严格次小生成树时,是对于每一条不在最小生成树中的边,将它加入后,再从构成的环中去掉一个除了它之外最大的边,所有这样构成的树中边权和最小的就是次小生成树了。然而这次要求严格次小生成树,这就意味着它的边权和必须严格大于最小生成树,所以对于每条不在最小生成树中的边,如果它和它两个端点在最小生成树上路径中最大边相等,就要用严格次大的边来作为被删掉的边。树上两点间严格次大的边权可以用倍增LCA来找,具体的预处理方法这里解释不清,请看代码吧。这样一来,时间复杂度是O(MlogN)的,可以通过此题。
    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define ll long long
    using namespace std;
    int n,m;
    ll totalsum,ans=2000000000;
    int first[100010],tot=0,f[100010];
    int fa[100010][21]={0},mx[100010][21]={0},smx[100010][21]={0},dep[100010]={0};
    struct edge {int v,next;ll d;} e[200010];
    struct forsort {int x,y;ll z;} a[300010];
    bool chosen[300010]={0};
    
    bool cmp(forsort a,forsort b)
    {
        return a.z<b.z;
    }
    
    int find(int x)
    {
        int r=x,i=x,j;
        while(f[r]!=r) r=f[r];
        while(i!=r)
        {
            j=f[i];
            f[i]=r;
            i=j;
        }
        return r;
    }
    
    void merge(int a,int b)
    {
        int fa=find(a),fb=find(b);
        f[fa]=fb;
    }
    
    void insert(forsort a)
    {
        e[++tot].v=a.y,e[tot].d=a.z,e[tot].next=first[a.x],first[a.x]=tot;
        e[++tot].v=a.x,e[tot].d=a.z,e[tot].next=first[a.y],first[a.y]=tot;
    }
    
    void kruskal()
    {
        sort(a+1,a+m+1,cmp);
        int now=0;totalsum=0;
        for(int i=1;i<=n;i++) f[i]=i;
        for(int i=1;i<=m;i++)
        {
            if (now==n-1) break;
            int fx=find(a[i].x),fy=find(a[i].y);
            if (fx!=fy)
            {
                now++;
                totalsum+=a[i].z;
                chosen[i]=1;
                insert(a[i]);
                merge(a[i].x,a[i].y);
            }
        }
    }
    
    void dfs(int v)
    {
        smx[v][0]=-1;
        for(int i=first[v];i;i=e[i].next)
            if (e[i].v!=fa[v][0])
            {
                fa[e[i].v][0]=v;
                mx[e[i].v][0]=e[i].d;
                dep[e[i].v]=dep[v]+1;
                dfs(e[i].v);
            }
    }
    
    void calc(ll &val1,ll val2,ll &val3,ll val4)
    {
        ll v1,v2;
        v1=max(val1,val2);
        if (val3>=val2) v2=val3;
        else if (val4>=val1) v2=val4;
             else if (val1!=val2) v2=min(val1,val2);
                  else v2=max(val3,val4);
        val1=v1,val3=v2;
    }
    
    ll query(ll d,int x,int y)
    {
        ll mxx=0,smxx=0;
        if (dep[x]<dep[y]) swap(x,y);
        for(int i=20;i>=0;i--)
            if (dep[fa[x][i]]>=dep[y])
            {
                calc(mxx,mx[x][i],smxx,smx[x][i]);
                x=fa[x][i];
            }
        if (x!=y)
        {
            for(int i=20;i>=0;i--)
                if (fa[x][i]!=fa[y][i])
                {
                    calc(mxx,mx[x][i],smxx,smx[x][i]);
                    calc(mxx,mx[y][i],smxx,smx[y][i]);
                    x=fa[x][i],y=fa[y][i];
                }
            calc(mxx,mx[x][0],smxx,smx[x][0]);
            calc(mxx,mx[y][0],smxx,smx[y][0]);
        }
        if (d==mxx&&smxx!=-1) return smxx;
        else return mxx;
    }
    
    void work()
    {
        dfs(1);
        for(int i=1;i<=20;i++)
            for(int j=1;j<=n;j++)
            {
                fa[j][i]=fa[fa[j][i-1]][i-1];
                ll val1=mx[j][i-1],val2=mx[fa[j][i-1]][i-1],val3=smx[j][i-1],val4=smx[fa[j][i-1]][i-1];
                mx[j][i]=max(val1,val2);
                if (val3>=val2) smx[j][i]=val3;
                else if (val4>=val1) smx[j][i]=val4;
                     else if (val1!=val2) smx[j][i]=min(val1,val2);
                          else smx[j][i]=max(val3,val4);
            }
        for(int i=1;i<=m;i++)
            if (!chosen[i])
            {
                ll val=query(a[i].z,a[i].x,a[i].y);
                if (val!=-1) ans=min(ans,a[i].z-val);
            }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].z);
    
        kruskal();
        work();
        printf("%lld",totalsum+ans);
    
        return 0;
    }
    
  • 相关阅读:
    解决windows 下Java编译和运行版本不一致的错误has been compiled by a more recent version
    解决两个OpenCV 报错 (raise.c and GTK) ,重新安装和编译
    Java|如何使用“Java”爬取电话号码(转载)
    Java手机号码工具类(判断运营商、获取归属地)以及简要的原理跟踪(转载)
    Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
    mysql分库分表
    python将html文件转换为pdf
    mysql获取字段名和对应的注释
    mysql大表查询慢对应方案
    harbor安装
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793597.html
Copyright © 2011-2022 走看看