P4180严格次小生成树 0pts玄关求调
  • 板块学术版
  • 楼主M_Jun
  • 当前回复1
  • 已保存回复1
  • 发布时间2025/1/17 10:53
  • 上次更新2025/1/17 14:51:26
查看原帖
P4180严格次小生成树 0pts玄关求调
1098953
M_Jun楼主2025/1/17 10:53

rt,记录详情


为了切掉这道题,我已经研究了一上午,让 某ChatCPT 帮我看哪出了问题,憋了两个小时愣是放出来个屁。


样例(没过):

输入:

5 6
1 2 1 
1 3 2 
2 4 3 
3 5 4 
3 4 3 
4 5 6 

输出:

11

我的输出:

1


代码如下:

#include <algorithm>
#include <iostream>
#include <vector>
#include <cstdio>
#include <cmath>

using namespace std ;

// 给 long long 起个新名字
const int INF = 0x7fffffff ;
typedef long long lint ;

// LCA 用的变量
vector<int> father ;

// 求最小生成树用的变量
vector<bool> use ;
lint N, M ;
lint mst ;

// 求次小生成树用的变量
vector<int> dep ;
vector<vector<int>> anc ;
vector<vector<lint>> m1, m2 ;

// DFS 用的变量 (用来标记节点是否被访问过)
vector<bool> visited ;

// 主函数用的变量
lint ans ;
lint n, m ;
lint u, v, w ;

// 存图用的变量
int cnt ;
vector<int> head ;
struct Node_edge {
    lint next ;
    lint u, v, w ;
    // 重载运算符
    bool operator<(const Node_edge& edge) const {
        return w < edge.w ;
    }
} ;
vector<Node_edge> edge, pool ;

// 链式前向星加边
void add_edge(int u, int v, int w) {
    cnt++ ;
    edge[cnt].u = u ;
    edge[cnt].w = w ;
    edge[cnt].next = head[u] ;
    head[u] = cnt ;
    return ;
}

// 找祖先
lint find(int now) {
    if (father[now] != now) {
        // 进行路径压缩
        father[now] = find(father[now]) ;
    }
    return father[now] ;
}

// LCA
void merge(int u, int v) {
    u = find(u) ;
    v = find(v) ;
    if (u != v) father[u] = v ;
    return ;
}

// 求最小生成树
void kruskal() {
    sort(pool.begin(), pool.end()) ;
    for (int i = 1 ; i <= m ; i++) {
        if (find(pool[i].u) != find(pool[i].v)) {
            mst += pool[i].w ;
            merge(pool[i].u, pool[i].v) ;
            // 重新建图
            add_edge(pool[i].u, pool[i].v, pool[i].w) ;
            add_edge(pool[i].v, pool[i].u, pool[i].w) ;  // 如果是无向图,需双向添加
            // 标记 i 这条边在最小生成树中
            use[i] = true ;
        }
    }
    return ;
}

// 查找最小生成树中 u 到向上跳 2^i 步路径上能够被取代的最大边
lint findbig(lint u, lint i, lint ma) {
    if (m1[u][i] != ma) return m1[u][i] ;
    return m2[u][i] ;
}

// DFS 预处理最大值 m1 和次大值 m2
void DFS(int u, int father, int w) {
    visited[u] = true ; // 标记当前节点为已访问
    anc[u][0] = father ;
    dep[u] = dep[father] + 1 ;
    m1[u][0] = w ;
    m2[u][0] = -INF ;

    for (int i = 1 ; i <= 20 ; i++) {
        anc[u][i] = anc[anc[u][i - 1]][i - 1] ;
        m1[u][i] = max(m1[u][i - 1], m1[anc[u][i - 1]][i - 1]) ;
        m2[u][i] = max(m2[u][i - 1], m2[anc[u][i - 1]][i - 1]) ;
        if (m1[u][i - 1] != m1[anc[u][i - 1]][i - 1])
            m2[u][i] = max(m2[u][i], min(m1[u][i - 1], m1[anc[u][i - 1]][i - 1])) ;
    }

    for (int i = head[u] ; i != 0 ; i = edge[i].next) {
        if (visited[edge[i].v]) continue ; // 如果已经访问过,跳过
        if (edge[i].v == father) continue ;
        DFS(edge[i].u, edge[i].v, edge[i].w) ;
    }
}

// 寻找最小生成树中 u 和 v 的 lca,并求出 u 到 v 路径上的最大值或次大值 res
lint lca(lint u, lint v, lint ma) {
    if (dep[u] < dep[v]) swap(u, v) ;
    lint res = -INF ;
    for (int i = 20 ; i >= 0 ; i--) {
        if (dep[anc[u][i]] < dep[v])
            continue ;
        // 更新 res
        res = max(res, findbig(u, i, ma)) ;
        u = anc[u][i] ;
    }
    if (u == v) return res ;
    for (int i = 20 ; i >= 0 ; i--) {
        if (anc[u][i] != anc[v][i]) {
            // 更新 res
            res = max(res, findbig(u, i, ma)) ;
            res = max(res, findbig(v, i, ma)) ;
            // 更新 u, v 的位置
            u = anc[u][i] ;
            v = anc[v][i] ;
        }
    }
    // 更新 res
    res = max(res, findbig(u, 0, ma)) ;
    res = max(res, findbig(v, 0, ma)) ;
    return res ;
}

// 主函数
signed main() {
	
    // 初始化(确保数组大小)
    cin >> n >> m ;
    father.resize(n + 1) ;
    use.resize(m + 1) ;
    dep.resize(n + 1) ;
    visited.resize(n + 1) ;
    anc.resize(n + 1, vector<int>(21)) ;
    m1.resize(n + 1, vector<lint>(21)) ;
    m2.resize(n + 1, vector<lint>(21)) ;
    edge.resize(m + 1) ;
    pool.resize(m + 1) ;
    head.resize(n + 1, 0) ;

    // 输入
    for (int i = 1 ; i <= m ; i++) {
        int a, b, c ;
        cin >> a >> b >> c ;
        pool[i].u = a ;
        pool[i].v = b ;
        pool[i].w = c ;
    }

    kruskal() ; // 求最小生成树
    DFS(1, 0, -INF) ; // DFS 预处理最大值 m1 和次大值 m2
    lint ans = INF ;
    for (int i = 1 ; i <= m ; i++) {
        // 如果用过了
        if (use[i]) continue ;
        // 否则
        ans = min(ans, mst + pool[i].w - lca(pool[i].u, pool[i].v, pool[i].w)) ;
    }

    cout << ans << '\n' ;

    return 0 ;
}

玄关求调

2025/1/17 10:53
加载中...