图论——次小生成树

图论——次小生成树

我们定义一个图的次小生成树(如果存在)为边权之和大于等于最小生成树的第二小生成树。

由于次小生成树和最小生成树之间只差一个边的改动,我们可以先求一边最小生成树,遍历所有未添加的边 ( u , v ) (u,v) (u,v),并计算路径 u → v u \to v uv中的最大边权,用这个边去替换最大边。在所有方案中取最小即可。

这样求的生成树是不严格次小生成树,如果要求严格次小生成树,我们必须维护路径上最大和次大边权,在所有方案中取最小即可。可以通过树链剖分,倍增,ST表实现。

P4180

#include <bits/stdc++.h>

#define FR freopen("in.txt", "r", stdin)
#define FW freopen("out.txt", "w", stdout)

using namespace std;

int pos[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long, long> pll;

ll W;

const ll mod = 1e9 + 7;

pll CH(ll p[4])
{
    sort(p, p + 4);
    pll ans = make_pair(p[3], p[3]);

    for (int i = 2; i >= 0; i--)
    {
        ans.second = p[i];

        if (ans.second != ans.first)
            break;
    }

    return ans;
}

int n, m;

struct Edge
{
    int from;
    int to;
    ll w;
    bool vis;
    bool operator<(const Edge &o) const
    {
        return w < o.w;
    }
} e[300005];

struct Edge2
{
    int to;
    int nxt;
    ll w;

} e2[600005];

int tot = 0;

int head[100005];

void add(int u, int v, ll w)
{
    tot++;
    e2[tot].to = v;
    e2[tot].nxt = head[u];
    e2[tot].w = w;
    head[u] = tot;
}

int pre[100005];

int find(int u)
{
    return u == pre[u] ? u : pre[u] = find(pre[u]);
}

int siz[100005];
int dep[100005];
int wson[100005];
int fa[100005];
ll st[100005][30];
ll st1[100005][30];

void dfs1(int u, int r)
{
    fa[u] = r;
    siz[u] = 1;
    dep[u] = dep[r] + 1;

    for (int ne = head[u]; ne; ne = e2[ne].nxt)
    {
        int to = e2[ne].to;

        if (to == r)
            continue;

        dfs1(to, u);
        siz[u] += siz[to];

        if (siz[to] > siz[wson[u]])
        {
            wson[u] = to;
        }
    }
}

int dfn[100005];
int top[100005];
int IDK;

void dfs2(int u, int r)
{
    dfn[u] = ++IDK;

    if (wson[u] != 0)
    {
        top[wson[u]] = top[u];
        dfs2(wson[u], u);
    }

    for (int ne = head[u]; ne; ne = e2[ne].nxt)
    {
        int to = e2[ne].to;

        if (to == r || to == wson[u])
            continue;

        top[to] = to;
        dfs2(to, u);
    }
}

void dfs3(int u, int r)
{
    for (int ne = head[u]; ne; ne = e2[ne].nxt)
    {
        int to = e2[ne].to;

        if (to == r)
            continue;

        st[dfn[to]][0] = e2[ne].w;
        st1[dfn[to]][0] = e2[ne].w;
        dfs3(to, u);
    }
}

void buildST()
{
    for (int e = 1; e < 30; e++)
    {
        for (int i = 1; i + (1 << e) - 1 <= n; i++)
        {
            ll p[4] = {st[i][e - 1], st1[i][e - 1],
                       st[i + (1 << (e - 1))][e - 1],
                       st1[i + (1 << (e - 1))][e - 1]};
            pll ans = CH(p);
            st[i][e] = ans.first;
            st1[i][e] = ans.second;
        }
    }
}

int LOG[100005];

pll query(int l, int r)
{
    int e = LOG[r - l + 1];
    ll p[4] = {st[l][e], st1[l][e], st[r - (1 << e) + 1][e],
               st1[r - (1 << e) + 1][e]};
    pll ans = CH(p);
    return ans;
}

pll LCA(int u, int v)
{
    pll ans;

    while (top[u] != top[v])
    {
        if (dep[top[u]] < dep[top[v]])
        {
            swap(u, v);
        }

        pll c = query(dfn[top[u]], dfn[u]);
        ll p[4] = {c.first, c.second, ans.first, ans.second};
        pll k = CH(p);
        ans.first = k.first;
        ans.second = k.second;
        u = fa[top[u]];
    }

    if (dep[v] > dep[u])
    {
        swap(u, v);
    }
    if (u == v)
        return ans;

    pll c = query(dfn[v] + 1, dfn[u]);
    ll p[4] = {c.first, c.second, ans.first, ans.second};
    pll k = CH(p);
    ans.first = k.first;
    ans.second = k.second;

    return ans;
}

int main()
{
    FR;
    scanf("%d %d", &n, &m);

    for (int i = 2; i < 100005; i++)
    {
        LOG[i] = LOG[i >> 1] + 1;
    }

    for (int i = 1; i <= n; i++)
    {
        pre[i] = i;
    }

    for (int i = 0; i < m; i++)
    {
        scanf("%d %d %lld", &e[i].from, &e[i].to, &e[i].w);
    }

    sort(e, e + m);

    for (int i = 0; i < m; i++)
    {
        int ur = find(e[i].from);
        int vr = find(e[i].to);

        if (ur == vr)
            continue;

        W += e[i].w;
        pre[ur] = vr;
        add(e[i].from, e[i].to, e[i].w);
        add(e[i].to, e[i].from, e[i].w);
        e[i].vis = true;
    }

    dfs1(1, 1);
    dfs2(1, 1);
    dfs3(1, 1);
    buildST();
    ll ans = 999999999999999999;

    for (int i = 0; i < m; i++)
    {
        if (!e[i].vis)
        {
            pll p = LCA(e[i].from, e[i].to);

            if (p.first != e[i].w)
            {
                ans = min(ans, W - p.first + e[i].w);
            }

            if (p.second != e[i].w)
            {
                ans = min(ans, W - p.second + e[i].w);
            }
        }
    }

    printf("%lld", ans);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值