zoukankan      html  css  js  c++  java
  • 秘密的牛奶运输

    https://loj.ac/problem/10068

    题目描述

      给出一张图,求它的严格次小生成树。

    思路

      我们考虑最小生成树和严格次小生成树的关系,由于它只要求出边权和,所以我们可以只求出一种最小生成树。我们假如已知最小生成树(T),那么其中一种严格次小生成树必定是断掉(T)一条边,再加入连接(T1)(T2)的第二边权的边,这样必定有一种是次小生成树。或者说,我们不断尝试加入一条边,这样就和最小生成树形成一棵基环树,我们从环中去掉最长的一条边(除加入的边),这样也会有一种是次小生成树。

      我们先来证明算法思路的正确性。首先,对于第一种思路我们很容易证明这一定还是一棵生成树,而对于每两个连通支的合并,除一次合并外选择都是最小边权,所以必定有一次合并会是次小生成树。而对于第二种思路,它也肯定是一棵生成树,我们也是类似的思想,相当于在(prim)时除一条边外选择了次小边,所以也是一棵次小生成树。其实两个思路本质是相同的。

      求次小生成树算法:(①Kruskal)(②)倍增;(③Prim)

      基本的公式是(SecondMST=min(MST+w(u, v)-Max(u, v))) (((u, v) MST))

      我们先讲倍增的思路。加入我们加入了((u,v)),我们必定要找到形成环上的的最大边和严格次大边,这样如果这条边的边权等于最大边就删去次大边,否则就删去最大边。对于求这个我们可以用倍增维护,每次维护最大边时比较简单,而次大边需要分两种:如果最大边相同,就取次大边中较大值;否则就取较小的最大边和另一个次大边中的较大值。

      再说(prim)(Kruskal)的思路。它们的基本思想都是在进行最小生成树时维护一个(Max)数组,表示最小生成树上两点路径上的最大边权。其实这个和倍增是有所重叠的,因为树上两点的边权可以用倍增求。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int MAXN=600,MAXM=1e4+10;
    const ll INF=(1LL<<50);
    struct Edge
    {
        int x,y,w;
    }e[MAXM];
    int fa[MAXN],nxt[MAXM],head[MAXN],to[MAXM],w[MAXM];
    int f[MAXN][21],dep[MAXN],tot;
    ll g[MAXN][21][2];
    bool used[MAXM];
    bool cmp(Edge x,Edge y)
    {
        return x.w<y.w;
    }
    int find(int x)
    {
        return fa[x]==x?x:fa[x]=find(fa[x]);
    }
    void add_edge(int x,int y,int v)
    {
        nxt[++tot]=head[x];
        head[x]=tot;
        to[tot]=y;
        w[tot]=v;
    }
    int cal(int x,int k)
    {
        int y=f[x][k];
        if(g[x][k][0]==g[y][k][0])
            return max(g[x][k][1],g[y][k][1]);
        else if(g[x][k][0]<g[y][k][0])
            return max(g[x][k][0],g[y][k][1]);
        else return max(g[x][k][1],g[y][k][0]);
    }
    void dfs(int u,int father)
    {
        dep[u]=dep[father]+1;
        for(int i=0;i<=19;i++)
        {
            f[u][i+1]=f[f[u][i]][i];
            g[u][i+1][0]=max(g[u][i][0],g[f[u][i]][i][0]);
            g[u][i+1][1]=cal(u,i);
        }
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            if(v==father)continue ;
            f[v][0]=u;
            g[v][0][0]=w[i];
            g[v][0][1]=-INF;
            dfs(v,u);
        }
    }
    int LCA(int x,int y)
    {
        if(dep[x]<dep[y])swap(x,y);
        for(int i=20;i>=0;i--)
        {
            if(dep[f[x][i]]>=dep[y])x=f[x][i];
            if(x==y)return x;
        }
        for(int i=20;i>=0;i--)
            if(f[x][i]!=f[y][i])
            {
                x=f[x][i];
                y=f[y][i];
            }
        return f[x][0];
    }
    ll qmax(int x,int y,int w)
    {
        ll res=-INF;
        for(int i=20;i>=0;i--)
            if(dep[f[x][i]]>=dep[y])
            {
                if(w!=g[x][i][0])res=max(res,g[x][i][0]);
                else res=max(res,g[x][i][1]);
                x=f[x][i];
            }
        return res;
    }
    int main() 
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
        sort(e+1,e+m+1,cmp);
        for(int i=1;i<=n;i++)
            fa[i]=i;
        int cnt=0;tot=0;
        ll sum=0;
        for(int i=1;i<=m;i++)
        {
            int fx=find(e[i].x),fy=find(e[i].y);
            if(fx!=fy)
            {
                fa[fx]=fy;
                cnt++;
                add_edge(e[i].x,e[i].y,e[i].w);
                add_edge(e[i].y,e[i].x,e[i].w);
                used[i]=1;
                sum+=e[i].w;
                if(cnt==n-1)break ;
            }
        }
        dfs(1,0);
        ll ans=INF;
        for(int i=1;i<=m;i++)
            if(!used[i])
            {
                int lca=LCA(e[i].x,e[i].y);
                int val=max(qmax(e[i].x,lca,e[i].w),qmax(e[i].y,lca,e[i].w));
                ans=min(ans,sum-val+e[i].w);
            }
        printf("%lld",ans);
        return 0;
    }
    
  • 相关阅读:
    Win7 安装 IIS 7.5 及部署 .NET 程序到 IIS 的内容杂记
    乐嘉,你快回来
    AMD将要嫁他人?
    高精度运算(运算符重载)
    魔数卡牌
    C语言"#","#@"和"##"在宏定义中的使用技巧
    CentOS7下安装FTP服务
    Linux下FTP的安装和登陆
    怎么让FOXMAIL关了以后在右下角自动收取邮件
    在CentOS7上安装ftp服务器用于保存服务端上传的图片。
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11794869.html
Copyright © 2011-2022 走看看