【模板】严格次小生成树(LCA+倍增+kruskal)

模板题啥的就自己去洛谷上搜搜就好了

直接上代码

#include <bits/stdc++.h>

using namespace std;

const int MAXV = 1e5 + 5, MAXE = 6e5 + 5, DEP = 18;
const int INF = 0x7f7f7f7f;
typedef long long ll;
typedef pair<int, int> pii;
int V, E;
pair<int, pii> Es[MAXE];
bool used[MAXE];
inline pii combine(const pii& a, const pii& b)
{ // 合并区间严格最大值次大值信息
    pii ans(max(a.first, b.first), max(a.second, b.second));
    if(min(a.first, b.first) != ans.first)
        ans.second = max(ans.second, min(a.first, b.first));
    return ans;
}
namespace T
{
struct Edge
{
    int to, last, l;
    void set(int _to, int _last, int _l = 0) { to = _to, last = _last, l = _l; }
} edges[2 * MAXV];
int top, head[MAXV];
void init() { top = 0, fill(head + 1, head + V + 1, -1); }
void add(int fr, int to, int l = 0)
{
    edges[top].set(to, head[fr], l);
    head[fr] = top++;
}
int fa[MAXV][DEP], depth[MAXV];
pii lenz[MAXV][DEP];
void build_lca(int fr)
{
    for(int i = 1; (1 << i) <= depth[fr]; ++i)
    {
        fa[fr][i] = fa[fa[fr][i - 1]][i - 1];
        lenz[fr][i]=combine(lenz[ fa[fr][i-1] ][i-1],lenz[fr][i-1]);
    }
    for(int i = head[fr], to; ~i; i = edges[i].last)
    {
        to = edges[i].to;
        if(to == fa[fr][0]) continue;
        fa[to][0] = fr, depth[to] = depth[fr] + 1;
        lenz[to][0] = pii(edges[i].l, 0);
        build_lca(to);
    }
}
pii lca(int a, int b)
{
    pii ans;
    if(depth[a] < depth[b]) swap(a, b);
    for(int i = DEP - 1; i >= 0; --i)
        if(depth[a] - (1 << i) >= depth[b])
        {
            ans = combine(ans, lenz[a][i]);
            a = fa[a][i];
        }
    if(a == b) return ans;
    for(int i = DEP - 1; i >= 0; --i)
        if(fa[a][i] != fa[b][i])
        {
            ans = combine(ans, combine(lenz[b][i], lenz[a][i]));
            a = fa[a][i], b = fa[b][i];
        }
    ans = combine(ans, combine(lenz[a][0], lenz[b][0]));
    return ans;
}
}
namespace K
{
int uset[MAXV];
int get_fa(int x)
{
    if(uset[x] == x) return x;
    return uset[x] = get_fa(uset[x]);
}
ll Kruskal()
{
    sort(Es, Es + E);
    for(int i = 1; i <= V; ++i) uset[i] = i;
    int fr, to, x, y, l, cnt = 0;
    ll ans = 0;
    for(int i = 0; i < E; ++i)
    {
        tie(fr, to) = Es[i].second;
        x = get_fa(fr), y = get_fa(to);
        if(x == y) continue;
        ans += (l = Es[i].first);
        used[i] = 1, ++cnt;
        uset[y] = x;
        T::add(fr, to, l), T::add(to, fr, l);
    }
    if(cnt != V - 1) return -1;
    return ans;
}
}
int main()
{
    scanf("%d %d", &V, &E);
    for(int i = 0, fr, to, l; i < E; ++i)
    {
        scanf("%d %d %d", &fr, &to, &l);
        Es[i] = pair<int, pii>(l, pii(fr, to));
    }
    T::init();
    ll mst = K::Kruskal();
    T::build_lca(1);
    int md, smd, diff = INF;
    for(int i = 0, fr, to; i < E; ++i)
        if(!used[i])
        {
            tie(fr, to) = Es[i].second;
            tie(md, smd) = T::lca(fr, to);
            if(Es[i].first > md) diff = min(diff, Es[i].first - md);
            else if(smd && Es[i].first > smd)
                diff = min(diff, Es[i].first - smd);
        }
    printf("%lld\n", mst + diff);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值