严格次小生成树(模板)-洛谷4180

传送门

dr就这样咕咕咕了好几天

(一时咕,一时爽,一直咕,那就gg了)

我才不会承认

我看题(概念)看了好久都没懂

-----------------------------------------------------

一、定义

  给出一个图

  可以形成若干个树

  将形成的树 按总比边权和的大小 从小到大排序

  第二小的就是次小生成树

这时需要注意啦:

  若只是按排完序后的第二个树

  得到的是 不严格次小生成树

  因为 有可能排序后的第二个树 和 第一个树相等

  那么

  严格次小生成树 就是 和 第一棵树不等的 剩余树中的 最小的树

(终于整明白定义了,就开始想想怎么做吧)

二、算法

先求出最小生成树

(个人喜欢用kruskal)

然后枚举出非树边

把非树边加入到树中

就会形成一个环

就要删边

(看看 哪次 加入非树边再删边 得到的生成树 是最小的<当然要大于最小生成树啦>)

(每枚举到一条非树边 就是 一种情况的(一棵树)的判断)

删边的原则?

如果加进来的边与环上最大值不同,直接删掉最大值

如果加进来的边与环上最大值相同,就删掉次大值

如何高效的去找要删的边咧?(维护最大值和次大值)

倍增LCA!!

fa维护LCA

maxi维护最大值

mini维护次大值

#include<cstdio>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;

const ll N = 400010;
const ll M = 900010;
const ll INF = 2147483647000000;

inline ll read()//快读 
{
    ll sum = 0,p = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
            p = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        (sum *= 10) += ch - '0';
        ch = getchar();
    }
    return sum * p;
}

struct edge
{
    ll u,v,d;
    ll next;
}G[N << 1];
ll tot,head[N];
edge A[M<<1];
bool vis[M<<1];
ll fa[N];
ll f[N][19]; 
ll maxi[N][19];
ll mini[N][19];
ll deep[N];
ll n,m;

inline bool cmp(edge x,edge y)//快排 
{
    return x.d < y.d;
} 

inline ll getfa(ll o)//找根节点 
{
    if(o == fa[o])
        return o;
    else
        return fa[o] = getfa(fa[o]);
}

inline void add(ll u,ll v,ll d)//加边 
{
    G[++tot].u = u;
    G[tot].v = v;
    G[tot].d = d;
    G[tot].next = head[u];
    head[u] = tot;
}

inline void dfs(ll o,ll x)//预处理 
{
    f[o][0] = x;
    for(ll i = head[o];i;i = G[i].next)
    {
        ll v = G[i].v;
        if(v == x)
            continue;
        deep[v] = deep[o] + 1LL;
        maxi[v][0] = G[i].d;
        mini[v][0] = -INF;
        dfs(v,o);
    }
}

inline void cal()//倍增的思想求出最大值和次大值 
{
    for(ll i = 1;i <= 18;i++)
        for(ll j = 1;j <= n;j++)
        {
            f[j][i] = f[f[j][i - 1]][i - 1];
            maxi[j][i] = max(maxi[j][i - 1],maxi[f[j][i - 1]][i - 1]);
            mini[j][i] = max(mini[j][i - 1],mini[f[j][i - 1]][i - 1]);
            if(maxi[j][i - 1] > maxi[f[j][i - 1]][i - 1])
                mini[j][i] = max(mini[j][i],maxi[f[j][i - 1]][i - 1]);
            else
            if(maxi[j][i - 1] < maxi[f[j][i - 1]][i - 1])
                mini[j][i] = max(mini[j][i],maxi[j][i - 1]);
        }
}

inline ll lca(ll x,ll y)//倍增lca 
{
    if(deep[x] < deep[y])
        swap(x,y);
    for(ll i = 18;i >= 0;i--)
        if(deep[f[x][i]] >= deep[y])
            x = f[x][i];
    if(x == y)
        return x;
    for(ll i = 18;i >= 0;i--)
        if(f[x][i] ^ f[y][i])
        {
            x = f[x][i];
            y = f[y][i];
        }
    return f[x][0];
}

inline ll qmax(ll u,ll v,ll maxx)//找最大值(或次大值)-- 倍增的思想 
{
    ll ans = -INF;
    for(ll i = 18;i >= 0;i--)
    {
        if(deep[f[u][i]] >= deep[v])
        {
            if(maxx != maxi[u][i])
                ans = max(ans,maxi[u][i]);
            else
                ans = max(ans,mini[u][i]);
            u = f[u][i];
        }
    }
    return ans;
}

int main()
{
    n = read(),m = read();
    for(ll i  = 1;i <= m;i++)
        A[i].u = read(),A[i].v = read(),A[i].d = read();
    sort(A+1,A+m+1,cmp);
    for(ll i = 1;i <= n;i++)
        fa[i] = i;
    ll cnt = 0LL;
    for(ll i = 1;i <= m;i++)//kruscal 
    {
        ll u = getfa(A[i].u);
        ll v = getfa(A[i].v);
        if(u != v)
        {
            cnt += A[i].d;
            fa[u] = v;
            add(A[i].u,A[i].v,A[i].d);
            add(A[i].v,A[i].u,A[i].d);
            vis[i] = true;
        }
    }
    mini[1][0] = -INF;
    deep[1] = 1;
    dfs(1,-1);
    cal();//维护最大值和次大值 
    ll ans = INF;
    for(ll i = 1;i <= m;i++)
    {
        if(!vis[i])//没有被加过边(不在生成图中) 
        {
            ll u = A[i].u;
            ll v = A[i].v;
            ll d = A[i].d;
            ll o = lca(u,v);
            ll maxu = qmax(u,o,d);
            ll maxv = qmax(v,o,d);
            ans = min(ans,cnt - max(maxu,maxv) + d);
        }
    } 
    printf("%lld",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/darlingroot/p/10666595.html