次小生成树 Tree HYSBZ - 1977 lca + kruskal

小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
Input

第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
Output

包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

Sample Input
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
11
Hint

数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。


求严格次小生成树:
首先明显要先求出 最小生成树--------kruskal ;
然后对于每一个非树边,加进去之后显然形成环,
假设改变的端点分别为 u, v ,那么与 lca( u,v ) 形成环;
如果该边权值>环上的最大值,那么将其加入候选答案;
如果该边权值=环上的最大值,那么将次大值加入候选答案;
所以我们的任务就是求出最大值及次大值;
考虑lca思路,我们同样可以利用倍增来进行 dp 实现;
设 dp1[ i ][ k ]表示从 i 开始 2^k 倍祖先的 max 值,进行转移即可;
同理用 dp2[ i ][ k ] 表示次大值;

#include <bits/stdc++.h>

using namespace std;
#define maxn 500005
#define inf 9999999999999999
#define rdllt(x) scanf("%lld",&x)
typedef long long ll;

int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}

struct node
{
    ll from,to,w;
    ll nxt;
}g[maxn<<1];

ll cnt,head[maxn];

void addedge(int u,int v,int w)
{
    g[++cnt].from=u;g[cnt].to=v;g[cnt].w=w;
    g[cnt].nxt=head[u];
    head[u]=cnt;
}

ll fa[maxn][20];
int n,m;
ll dp1[maxn][20],dp2[maxn][20];
int t;
ll depth[maxn];

void init()
{
    t=(int)(log2(n))+1;
    depth[1]=1;
}

void dfs(int rt)
{
    for(int i=1;i<=t;i++){
       fa[rt][i]=fa[fa[rt][i-1]][i-1];
    }
    for(int i=head[rt];i;i=g[i].nxt){
        int v=g[i].to;
        if(v==fa[rt][0])continue;
        depth[v]=depth[rt]+1;fa[v][0]=rt;
        dp1[v][0]=g[i].w;dp2[v][0]=-inf;
        dfs(v);
    }
}

void sol()
{
    for(int i=1;i<=t;i++){
        for(int j=1;j<=n;j++){
            dp1[j][i]=max(dp1[j][i-1],dp1[fa[j][i-1]][i-1]);
            dp2[j][i]=max(dp2[j][i-1],dp2[fa[j][i-1]][i-1]);
            if(dp1[j][i-1]>dp1[fa[j][i-1]][i-1]){
                dp2[j][i]=max(dp2[j][i],dp1[fa[j][i-1]][i-1]);
            }
            else if(dp1[j][i-1]<dp1[fa[j][i-1]][i-1]){
                dp2[j][i]=max(dp2[j][i],dp1[j][i-1]);
            }
        }
    }
}

ll Lca(ll x,ll y)
{
    if(depth[x]>depth[y])swap(x,y);
    for(int i=t;i>=0;i--){
        if(depth[fa[y][i]]>=depth[x])y=fa[y][i];
    }
    if(x==y)return x;
    for(int i=t;i>=0;i--){
        if(fa[x][i]!=fa[y][i]){
            x=fa[x][i];y=fa[y][i];
        }
    }
    return fa[x][0];
}

ll query(ll u,ll v,ll maxx)
{
    ll ans=-inf;
    for(int i=t;i>=0;i--){
        if(depth[fa[u][i]]>=depth[v]){
            if(maxx!=dp1[u][i])ans=max(ans,dp1[u][i]);
            else ans=max(ans,dp2[u][i]);
            u=fa[u][i];
        }
    }
    return ans;
}

bool cmp(node a,node b)
{
    return a.w<b.w;
}

node eg[maxn<<1];
ll father[maxn];
bool used[maxn];

ll findfa(ll x)
{
    if(x==father[x])return x;
    else return father[x]=findfa(father[x]);
}


int main()
{
    cin>>n>>m;init();
    for(int i=0;i<=n;i++)father[i]=i;
    for(int i=1;i<=m;i++){
        rdllt(eg[i].from);rdllt(eg[i].to);rdllt(eg[i].w);
   
    }
    // kruskal
    sort(eg+1,eg+1+m,cmp);
    ll Sum=0;
    for(int i=1;i<=m;i++){
        ll p=findfa(eg[i].from),q=findfa(eg[i].to);
        if(p!=q){
            father[p]=q;Sum+=eg[i].w;
            addedge(eg[i].from,eg[i].to,eg[i].w);addedge(eg[i].to,eg[i].from,eg[i].w);
            used[i]=true;
        }
    }
    dp2[1][0]=-inf;
    dfs(1);sol();
    ll Ans=inf;
    for(int i=1;i<=m;i++){
        if(!used[i]){
            ll u=eg[i].from;
            ll v=eg[i].to;
            ll w=eg[i].w;
            ll LCA=Lca(u,v);
            ll Max1=query(u,LCA,w);
            ll Max2=query(v,LCA,w);
            Ans=min(Ans,Sum+w-max(Max1,Max2));
        }
    }
    cout<<(ll)Ans<<endl;
    return 0;
}





猜你喜欢

转载自blog.csdn.net/qq_40273481/article/details/83022536