严格次小生成树


模板题链接

很久没有写过这么长的代码了
重要部分就是对倍增 LCA 的改动

以下是代码,维护了最大边权和次大边权

// Created by qyy on 2024/6/4.

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

#define PII pair<int, int>
#define endl "\n"

const long long inf = 0x3f3f3f3f3f3f3f3f;
const int N = 3e5 + 10;
const int mod = 1e9 + 7;
#define int long long

int n, m, f[N];

int ekcnt = 0;
struct Edge_Kru{
    int from, to;
    ll val;
    bool flag;
}ek[N];

int head[N], cnt;
struct Edge{
    int from, to, nxt;
    ll val;
}e[N];
void add(int u, int v, ll val){
    e[++cnt].from = u;
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    e[cnt].val = val;
    head[u] = cnt;
}

bool cmp(Edge_Kru a, Edge_Kru b){
    return a.val < b.val;
}

int find_set(int x){
    if(f[x] != x) f[x] = find_set(f[x]);
    return f[x];
}
void merge_set(int x, int y){
    int fx = find_set(x), fy = find_set(y);
    if(fx != fy) f[fx] = fy;
}

ll dp[N][20], dp2[N][20]; // 分别是最大边权与次大边权

int fa[N][20], deep[N];
void dfs(int x, int father){
    deep[x] = deep[father] + 1;
    fa[x][0] = father;
    for(int i = 1; (1 << i) <= deep[x]; i++){
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
        dp[x][i] = max(dp[x][i - 1], dp[fa[x][i - 1]][i - 1]);
        dp2[x][i] = max(dp2[x][i - 1], dp2[fa[x][i - 1]][i - 1]);
        if(dp[x][i] > dp[x][i - 1]) dp2[x][i] = max(dp2[x][i], dp[x][i - 1]);
        if(dp[x][i] > dp[fa[x][i - 1]][i - 1]) dp2[x][i] = max(dp2[x][i], dp[fa[x][i]][i - 1]);
    }

    for(int i = head[x]; i != 0; i = e[i].nxt){
        int v = e[i].to;
        if(v != father)
        {
            dp[v][0] = e[i].val;
            // 问题是 次大的该怎么维护呢。
            dp2[v][0] =  0;
            dfs(v, x);
        }
    }
}

// LCA 也得改,直接修改为查询函数
ll LCA(int x, int y, ll val){
    if(deep[x] < deep[y]) swap(x, y);
    // 目的找到第一个小于 val 的最大值
    ll ans = 0;
    for(int i = 19; i >= 0; i--)
        if(deep[x] - (1 << i) >= deep[y]){

            if(dp[x][i] == val){
                ans = max(ans, dp2[x][i]);
            }else{
                ans = max(ans, dp[x][i]);
            }
            x = fa[x][i];
        }

    if(x == y) return ans;
    // x 和 y 一起向上跳
    for(int i = 19; i >= 0; i--){
        if(fa[x][i] != fa[y][i]){
            if(dp[x][i] == val){
                ans = max(ans, dp2[x][i]);
            }else{
                ans = max(ans, dp[x][i]);
            }
            if(dp[y][i] == val){
                ans = max(ans, dp2[y][i]);
            }else{
                ans = max(ans, dp[y][i]);
            }

            x = fa[x][i];
            y = fa[y][i];
        }
    }
    if(dp[x][0] == val){
        ans = max(ans, dp2[x][0]);
    }else{
        ans = max(ans, dp[x][0]);
    }
    if(dp[y][0] == val){
        ans = max(ans, dp2[y][0]);
    }else{
        ans = max(ans, dp[y][0]);
    }
    return ans;
}

void solve() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++){
        f[i] = i;
    }
    for(int i = 1; i <= m; i++){
        int u, v;
        ll val;
        cin >> u >> v >> val;
        if(u != v)
            ek[++ekcnt] = {u, v, val, false};
    }
    ll ans = 0;
    int begid = 0;
    sort(ek + 1, ek + 1 + ekcnt, cmp);
    for(int i = 1; i <= ekcnt; i++){
        int u = ek[i].from, v = ek[i].to;
        ll val = ek[i].val;
        int fu = find_set(u), fv = find_set(v);
        if(fu != fv){
            ek[i].flag = true;
            merge_set(u, v);
            add(u, v, val);
            add(v, u, val);
            begid = i + 1;
            ans += val;
        }
    }
    // 建树建完了
    dfs(1, 0);

    ll ans2 = inf;
    for(int i = 1; i <= ekcnt; i++){
        if(ek[i].flag) continue;
        int u = ek[i].from, v = ek[i].to;
        if(u == v) continue;
        ll val = ek[i].val;
        ans2 = min(ans2, val - LCA(u, v, val));
    }
    cout << ans + ans2 << endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t = 1;
    //cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}


我应该再也获得不了这样的机会了

posted @   9102700  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
  1. 1 鼓楼 赵雷
  2. 2 我们的歌 王力宏
  3. 3 老街 李荣浩
  4. 4 周杰伦
  5. 5 可惜没如果 林俊杰
  6. 6 不将就 李荣浩
  7. 7 南方姑娘 赵雷
  8. 8 南方姑娘(弹唱版) 赵雷
  9. 9 如果可以 韦礼安
  10. 10 写给黄淮 邵帅
  11. 11 我想念 汪苏泷
  12. 12 雨天 汪苏泷
  13. 13 雨天雨天 汪苏泷
  14. 14 成都 赵雷
我们的歌 - 王力宏
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 王力宏/陈信延

作曲 : 王力宏

编曲 : 王力宏

已经听了一百遍

已经听了一百遍

怎么听都不会倦

从白天唱到黑夜

你一直在身边

如果世界太危险

只有音乐最安全

带着我进梦里面

让歌词都实现

无论是开心还是难过我的爱一直不变

不必担心时间流逝带走一切

无论是Hip-Hop 还是摇滚我的爱一直不变

所有美好回忆记录在里面

这种 Forever Love 那么深

我们的歌 那么真

无国界跨时代

再也不会叫我Kiss Goodbye

要每一句能够动人心弦 Yeah

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

已经听了一百遍

已经听了一百遍

怎么听都不会倦

从白天唱到黑夜

你一直在身边

如果世界太危险

只有音乐最安全

带着我进梦里面

让歌词都实现

无论是开心还是难过我的爱一直不变

不必担心时间流逝带走一切

无论是Hip-Hop 还是摇滚我的爱一直不变

所有美好回忆记录在里面

这种 Forever Love 那么深

我们的歌 那么真

无国界跨时代

再也不会叫我Kiss Goodbye

要每一句能够动人心弦 Yeah

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

情人总分分合合

情人总分分合合

可是我们却越爱越深

认识你 让我的幸福如此悦耳

能不能不要切歌

继续唱我们的歌​​

让感动一辈子都记得

电吉他/其他乐器:王力宏

鼓手:Eric Fawcrtt

贝斯:John Mumson

录音师/录音室:王力宏/Homeboy Music Studios,Taipei

OP:HIM Music Publishing Inc.

OP:Homeboy Music,Inc,Taiwan

SP:Sony Music Publishing(Pre)Ltd.Taiwan Branch

点击右上角即可分享
微信分享提示