一本通1555【例 4】次小生成树

1555:【例 4】次小生成树

时间限制: 1000 ms         内存限制: 524288 KB

题目描述

原题来自:BeiJing 2010 组队赛

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

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

输入格式

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

接下来 MM 行,每行三个数 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

数据范围与提示

对于全部数据,1N10^5,1M3×10^5,数据中无向图无自环,边权值非负且不超过 10^9

 

sol:严格次小生成树模板,我使用倍增做的,树剖也可以(表示不清楚别人为什么这么快)

#include <bits/stdc++.h>
using namespace std;
const int N=100005,M=300005,inf=0x3f3f3f3f;
int n,m;
struct Edge
{
    int U,V,Val;
}E[M];
inline bool cmp(Edge p,Edge q)
{
    return p.Val<q.Val;
}
int Father[N];
long long MST=0;
bool Edge_Bo[M];
inline int Get_Father(int x)
{
    return (Father[x]==x)?(x):(Father[x]=Get_Father(Father[x]));
}
struct Tree
{
    int tot,Next[M],to[M],head[N],val[M];
    inline void add(int x,int y,int z)
    {
        Next[++tot]=head[x];
        to[tot]=y;
        val[tot]=z;
        head[x]=tot;
        return;
    }
    int Depth[N],F[N][23];
    int Val_Zd[N][23],Val_Cd[N][23];
    inline void dfs(int x,int fa)
    {
        F[x][0]=fa; Depth[x]=Depth[fa]+1;
        int i;
        for(i=head[x];i;i=Next[i]) if(to[i]!=fa)
        {
            Val_Zd[to[i]][0]=val[i];
            Val_Cd[to[i]][0]=-inf;
            dfs(to[i],x);
        }
        return;
    }
    inline void Pre()
    {
        int i,j;
        dfs(1,0);
        for(i=1;i<=19;i++)
        {
            for(j=1;j<=n;j++)
            {
                F[j][i]=F[F[j][i-1]][i-1];
                Val_Zd[j][i]=max(Val_Zd[j][i-1],Val_Zd[F[j][i-1]][i-1]);
                Val_Cd[j][i]=max(Val_Cd[j][i-1],Val_Cd[F[j][i-1]][i-1]);
                if(Val_Zd[j][i-1]<Val_Zd[F[j][i-1]][i-1]) Val_Cd[j][i]=max(Val_Cd[j][i],Val_Zd[j][i-1]);
                else if(Val_Zd[F[j][i-1]][i-1]<Val_Zd[j][i-1]) Val_Cd[j][i]=max(Val_Cd[j][i],Val_Zd[F[j][i-1]][i-1]);
            }
        }
        return;
    }
    inline int Ask_Lca(int x,int y)
    {
        int i;
        if(Depth[x]<Depth[y]) swap(x,y);
        for(i=19;~i;i--) if(Depth[F[x][i]]>=Depth[y]) x=F[x][i];
        if(x==y) return x;
        for(i=19;~i;i--) if(F[x][i]!=F[y][i]) x=F[x][i],y=F[y][i];
        return F[x][0];
    }
    inline int Ask_Lower(int x,int y,int z)
    {
        int i,Now=x,ans=0;
        for(i=19;~i;i--) if(Depth[F[Now][i]]>=Depth[y])
        {
//            printf("Now=%d Val_Zd[Now][i]=%d BB=%d\n",Now,Val_Zd[Now][i],z);
//            printf("Now=%d Val_Cd[Now][i]=%d BB=%d\n",Now,Val_Cd[Now][i],z);
            if(Val_Zd[Now][i]<z) ans=max(ans,Val_Zd[Now][i]);
            else ans=max(ans,Val_Cd[Now][i]);
            Now=F[Now][i];
        }
        return ans;
    }
}T;
int main()
{
    int i;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++) Father[i]=i;
    for(i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        E[i]=(Edge){x,y,z};
    }
    sort(E+1,E+m+1,cmp);
    for(i=1;i<=m;i++)
    {
        int xx=Get_Father(E[i].U),yy=Get_Father(E[i].V);
        if(xx==yy) continue;
        Father[xx]=yy; MST+=E[i].Val; Edge_Bo[i]=1;
        T.add(E[i].U,E[i].V,E[i].Val); T.add(E[i].V,E[i].U,E[i].Val);
    }
//    printf("MST=%d\n",MST);
    T.Pre();
    long long ans=0x7ffffffffff;
    for(i=1;i<=m;i++) if(!Edge_Bo[i])
    {
        int x=E[i].U,y=E[i].V,z=E[i].Val;
        int ll=T.Ask_Lca(x,y);
//        printf("x=%d y=%d ll=%d\n",x,y,ll);
        int p1=T.Ask_Lower(x,ll,z),p2=T.Ask_Lower(y,ll,z);
//        printf("p1=%d p2=%d\n",p1,p2);
        ans=min(ans,MST-max(p1,p2)+z);
    }
    printf("%lld\n",ans);
    return 0;
}
/*
input
5 6 
1 2 1 
1 3 2 
2 4 3 
3 5 4 
3 4 3 
4 5 6
output
11
*/
View Code

 

posted @ 2019-02-05 16:14  yccdu  阅读(568)  评论(1编辑  收藏  举报