代码是次小生成树(LCA优化)的板子,具体见下 。
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std ;
// 给 long long起个新名字
typedef long long lint ;
// 存数组的参考变量
const lint MAXN = 1e5 + 5 ;
const lint MAXM = 3e5 + 5 ;
const lint INF = 0x7fffffff ;
// LCA 用的变量
int father[MAXN] ;
// 求最小生成树用的变量
bool use[MAXM] ;
lint N, M ;
lint mst ;
// 求次小生成树用的变量
int dep[MAXN] ;
int anc[MAXN][25] ;
lint m1[MAXN][25] ;
lint m2[MAXN][25] ;
// 主函数用的变量
lint ans ;
lint n, m ;
lint u, v, w ;
// 存图用的变量
int cnt ;
int head[MAXN] ;
struct Node_edge {
lint next ;
lint u, v, w ;
// 重载运算符
bool operator< (const Node_edge &edge) const {
return w < edge.w ;
}
} edge[MAXN * 2], pool[MAXM] ;
// 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) { // now 指现在的位置
if(father[now]) return father[now] ;
father[now] = find(father[now]) ;
return father[now] ;
}
// LCA
void merge(int u, int v) {
u = find(u) ;
v = find(v) ;
father[u] = v ;
}
// 求最小生成树
void kruskal() {
sort(pool + 1, pool + m + 1) ;
for(int i = 1 ; i <= m ; i ++) {
u = pool[i].u ;
v = pool[i].v ;
w = pool[i].w ;
if(find(u) != find(v)) {
mst += w ;
merge(u, v) ;
// 重新建图
add_edge(u, v, w) ;
add_edge(u, v, 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(lint u, lint father, lint w) {
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]])
m2[u][i] = max(m2[u][i], min(m1[u][i - 1], m1[anc[u][i - 1]])) ;
}
for(int i = head[u] ; i != 0 ; i = edge[i].next) {
if(edge[i].v == father) continue ;
dfs(edge[i].u, edge[i].v, edge[i].w) ;
}
return ;
}
// 寻找最小生成树中 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] ;
u = anc[u][i] ;
}
}
// 更新 res
res = max(res, findbig(u, 0, ma)) ;
res = max(res, findbig(v, 0, ma)) ;
return res ;
}
// 主函数
signed main ( ) {
//初始化
for(int i = 1 ; i <= n ; i ++)
father[i] = i ;
// 输入
cin >> n >> m ;
for(int i = 1 ; i <= m ; i ++)
cin >> pool[i].u >> pool[i].v >> pool[i].w ;
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 + w - lca(pool[i].u, pool[i].v, pool[i].w)) ;
}
cout << ans ;
return 0 ;
}
报错信息见下。
玄关求调。