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 ;
}
玄关求调