• 次小生成树(lca)


    题目描述

    原题来自:BeiJing 2010 组队赛

    给定一张 N 个点 M 条边的无向图,求无向图的严格次小生成树。

    设最小生成树的边权之和为 sum,严格次小生成树就是指边权之和大于 sum 的生成树中最小的一个。

    输入格式

    第一行包含两个整数 N 和 M,表示无向图的点数与边数;

    接下来 M 行,每行三个数 x,y,z表示点 x和点 y 之间有一条边,边的权值为 z。

    输出格式

    包含一行,仅一个数,表示严格次小生成树的边权和。

    数据保证必定存在严格次小生成树。

    样例

    样例输入

    5 6 
    1 2 1 
    1 3 2 
    2 4 3 
    3 5 4 
    3 4 3 
    4 5 6

    样例输出

    11
    #include<bits/stdc++.h>
    const int MAXN=100005,MAXM=300005;
    using namespace std;
    int n,m,N;
    struct E{
        int u,v,f;
        long long val;
    }a[MAXM*2];
    bool mw(E a,E b)
    {
        return a.val<b.val;
    }
    struct Edge{
        int to,next;
        long long val;
    }edge[MAXM*2];
    int head[MAXN],num=0;
    int fa[MAXN],find();
    long long ans,mn=100000000000;
    int f[MAXN][25],dep[MAXN];
    long long d1[MAXN][25],d2[MAXN][25];
    inline void add(int u,int v,long long val)
    {
        edge[++num].next=head[u];
        edge[num].to=v;
        edge[num].val=val;
        head[u]=num;
        return;
    }
    inline int find(int a)
    {
        return a==fa[a]?a:fa[a]=find(fa[a]);
    }
    inline void grow()
    {
        int u,v,val,cnt=0;
        sort(a+1,a+m+1,mw);
        int r1,r2;
        for(register int i=1;i<=n;++i)fa[i]=i;
        for(register int i=1;i<=m;++i)
        {
            u=a[i].u,v=a[i].v,val=a[i].val;
            r1=find(u);
            r2=find(v);
            if(r1!=r2)
            {
                cnt++;
                a[i].f=1;
                add(u,v,val);
                add(v,u,val);
                fa[r1]=r2;
                ans+=val;
                if(cnt==n-1) break;
            }
        }
        return;
    }
    inline void dfs(int u)
    {
        for(register int i=1;i<=N;i++)
        {
            f[u][i]=f[f[u][i-1]][i-1];
            d1[u][i]=max(d1[u][i-1],d1[f[u][i-1]][i-1]);
            if(d1[u][i-1]==d1[f[u][i-1]][i-1])
                d2[u][i]=max(d2[u][i-1],d2[f[u][i-1]][i-1]);
            else 
            {
                d2[u][i]=min(d1[u][i-1],d1[f[u][i-1]][i-1]);
                d2[u][i]=max(d2[u][i-1],d2[u][i]);
                d2[u][i]=max(d2[u][i],d2[f[u][i-1]][i-1]);
            }
        }
        for(register int i=head[u];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==f[u][0])continue;
            f[v][0]=u;
            d1[v][0]=edge[i].val;
            dep[v]=dep[u]+1;
            dfs(v);
        }
        return;
    }
    inline int lca(int a,int b)
    {
        if(dep[a]>dep[b]) swap(a,b);
        for(register int i=N;i>=0;i--)
        {
            if(dep[a]<dep[b]&&dep[f[b][i]]>=dep[a]) b=f[b][i];
        }
        if(a==b)return a;
        for(register int i=N;i>=0;i--)
        {
            if(f[a][i]!=f[b][i]){a=f[a][i],b=f[b][i];}
        }
        return f[a][0];
    }
    inline void Try(int u,int father,long long val)
    {
        int t;
        t=dep[u]-dep[father];
        long long mx1=-1,mx2=0;
        for(int i=0;i<=N;i++)
        {
            if(t&(1<<i))//注意这一步!! 
            {
                if(mx1<=d1[u][i])
                {
                    mx2=mx1;
                    mx1=d1[u][i];
                    mx2=max(mx2,d2[u][i]);
                }
                else
                {
                    mx2=max(mx2,d1[u][i]);
                }
                u=f[u][i];//写的好神奇 
            }
        }
        if(mx1!=val)
        {
            if(mn>val-mx1)
            {
                mn=val-mx1;
            }
        }
        else
        {
            if(mn>val-mx2)
            {
                mn=val-mx2;
            }
        }
        return;
    }
    inline void choose()
    {
        int f,u,v,val;
        for(register int i=1;i<=m;++i)
        {
            if(a[i].f==1)
              continue;
            u=a[i].u;
            v=a[i].v;
            val=a[i].val;
            f=lca(u,v);
            Try(u,f,val),Try(v,f,val);
        }
        return;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(register int i=1;i<=m;++i)
        {
            scanf("%d%d%lld",&a[i].u,&a[i].v,&a[i].val);
        }
        grow();
        N=floor(log(n+0.0)/log(2.0));
        dfs(1);
        choose();
        printf("%lld",ans+mn);
        return 0;
    }
  • 相关阅读:
    代理模式 值代理
    装饰者模式 检查装饰器循环
    装饰者模式 静态装饰组合
    装饰者模式 在依赖注入中体现
    代理模式 属性代理
    装饰器模式 动态组合装饰
    代理模式 请勿饮酒
    享元模式 文本编辑
    Mysql 字符串字段判断是否包含某个字符串的3种方法
    vuex存储保存数据、使用数据
  • 原文地址:https://www.cnblogs.com/719666a/p/9588037.html
Copyright © 2020-2023  润新知