题解 P4180 【【模板】严格次小生成树[BJWC2010]】

· · 题解

求次小生成树,首先我们要求出最小生成树

然后,我们显然要拿没被选中的边来替换最小生成树中的边。

当我们把一条边加进去的时候,必然会形成一个环,那么很简单,我们只用找出环上除新加边的且不与新加相等边最大边,差值就是加这条边的最小增加量。然后所有最小增加量再加上最小生成树的边权值,就是答案了。

同理,我们也可以这样求第三小生成树,而再小的话就不行了,因为有可能替换两条边要比替换一条边要小。而求第三小的话是不会出现这种情况的,原因如下:假设替换a增加量最小,b次小,c最大,显然a<b<a+b,而a+b是否小于c是不能确定的。

然后怎么找出环上除新加边的且不与新加相等边最大边呢?对于边(u,v)我们找出u,v的LCA:l,然后用倍增处理u->l,v->l两条路径,即可。但由于可能出现最大值和新加边的权值相等,所以我们还要预处理次大边,如下:

预处理:f不多说,v1为j倍增处理出的最大值,v2就是次大值。

for(int i=1;i<=21;i++)
        for(int j=1;j<=n;j++)
        {
            f[i][j]=f[i-1][f[i-1][j]];
            v1[i][j]=max(v1[i-1][j],v1[i-1][f[i-1][j]]);
            v2[i][j]=max(v2[i-1][j],v2[i-1][f[i-1][j]]);
            if(v1[i-1][j]>v1[i-1][f[i-1][j]]) v2[i][j]=max(v2[i][j],v1[i-1][f[i-1][j]]);
            else if(v1[i-1][j]<v1[i-1][f[i-1][j]]) v2[i][j]=max(v2[i][j],v1[i-1][j]);
        }

对于一条环的处理:

int get(int u,int v,int k)
{
    int res=-INF; 
    for(int i=20;i>=0;i--)
        if(dep[f[i][u]]>=dep[v])
        {
            if(k!=v1[i][u]) res=max(res,v1[i][u]);//不一样就和最大的取max
            else res=max(res,v2[i][u]);//否则和次大的取max
            u=f[i][u];
        }
    return res;
}

for(int i=1;i<=m;i++)
        if(!e[i].in)
        {
            int l=Lca(e[i].x,e[i].y);
            int res=max(get(e[i].x,l,e[i].z),get(e[i].y,l,e[i].z));
            ans=min(ans,e[i].z-res);
        }

OK,全部的代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const long long INF=214748364700000;
int n,m,sum=0,cnt=0;
int fa[100100],f[30][100100],dep[100100],v1[30][100100],v2[30][100100];
int head[200100],nxt[200100],to[200100],val[200100];
struct Edge
{
    int x,y,z,in;
}e[300100];
bool cmp(Edge a,Edge b) {return a.z<b.z;}
int find(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);
}
void addedge(int x,int y,int z)
{
    cnt++;
    nxt[cnt]=head[x];
    head[x]=cnt;
    to[cnt]=y;
    val[cnt]=z;
}
void dfs(int u,int pre)
{
    for(int i=head[u];i!=-1;i=nxt[i])
    {
        int v=to[i];
        if(v==pre) continue;
        dep[v]=dep[u]+1,f[0][v]=u;
        v1[0][v]=val[i],v2[0][v]=-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[i][x]]>=dep[y]) x=f[i][x];
    if(x==y) return x;
    for(int i=20;i>=0;i--)
        if(f[i][x]!=f[i][y]) x=f[i][x],y=f[i][y];
    return f[0][x];
}
int get(int u,int v,int k)
{
    int res=-INF; 
    for(int i=20;i>=0;i--)
        if(dep[f[i][u]]>=dep[v])
        {
            if(k!=v1[i][u]) res=max(res,v1[i][u]);
            else res=max(res,v2[i][u]);
            u=f[i][u];
        }
    return res;
}
signed main()
{
    memset(head,-1,sizeof(head));
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%lld%lld%lld",&e[i].x,&e[i].y,&e[i].z);
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        if(cnt==2*n-2) break;
        int fx=find(e[i].x),fy=find(e[i].y);
        if(fx!=fy)
        {
            fa[fx]=fy;
            sum+=e[i].z;
            e[i].in=1;
            addedge(e[i].x,e[i].y,e[i].z);
            addedge(e[i].y,e[i].x,e[i].z);
        }
    }
    dep[1]=1;dfs(1,0);
    for(int i=1;i<=21;i++)
        for(int j=1;j<=n;j++)
        {
            f[i][j]=f[i-1][f[i-1][j]];
            v1[i][j]=max(v1[i-1][j],v1[i-1][f[i-1][j]]);
            v2[i][j]=max(v2[i-1][j],v2[i-1][f[i-1][j]]);
            if(v1[i-1][j]>v1[i-1][f[i-1][j]]) v2[i][j]=max(v2[i][j],v1[i-1][f[i-1][j]]);
            else if(v1[i-1][j]<v1[i-1][f[i-1][j]]) v2[i][j]=max(v2[i][j],v1[i-1][j]);
        }
    int ans=INF;
    for(int i=1;i<=m;i++)
        if(!e[i].in)
        {
            int l=Lca(e[i].x,e[i].y);
            int res=max(get(e[i].x,l,e[i].z),get(e[i].y,l,e[i].z));
            ans=min(ans,e[i].z-res);
        }
    printf("%lld",ans+sum);
    return 0;
}