借鉴了大佬的博客:https://www.cnblogs.com/AK-ls/p/10509986.html
题目描述
小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) ∑e∈EMvalue(e)<∑e∈ESvalue(e)
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
思想概要
1. 先求出最小生成树
2. 然后枚举所有不在树上的边val加到树中,会形成一个环,在环找到最大边val1和次大边val2
- 如果val > val1,则sum + val - val1为一个候选答案
- 如果val = val1 ,则sum + val - val2为一个候选答案
3. 那么val1和val2要怎么求呢,就要用LCA倍增来维护 dis1[ i ][ j ]表示从 向上跳 次之间的最大边,dis2[ i ][ j ]表示从 向上跳 次之间的次大边(小于最大边)
fa[i][j] = fa[fa[i][j-1]][j-1];
dis1[i][j] = max(dis1[i][j-1], dis1[fa[i][j-1]][j-1]);
if(dis1[i][j-1] == dis1[fa[i][j-1]][j-1])
dis2[i][j] = max(dis2[i][j-1], dis2[fa[i][j-1]][j-1]);
else if(dis1[i][j-1] < dis1[fa[i][j-1]][j-1])
dis2[i][j] = max(dis1[i][j-1], dis2[fa[i][j-1]][j-1]);
else
dis2[i][j] = max(dis2[i][j-1], dis1[fa[i][j-1]][j-1]);
注意:这题的数据很大,边权和要开long long。
AC代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 5e5+10;
const int inf = 1e9+7;
int fa[maxn][22], pre[maxn], vis[maxn], deep[maxn], cnt,dis1[maxn][22], dis2[maxn][22];
int n, m, head[maxn];
struct Tree{
int u, v, w;
}t[maxn];
struct Edge{
int v, next, w;
}e[maxn*2];
void add(int u, int v, int w){
e[++cnt].v = v;
e[cnt].w = w;
e[cnt].next = head[u];
head[u] = cnt;
}
int Find(int x){
return x == pre[x] ? x:pre[x]=Find(pre[x]);
}
bool cmp(Tree a, Tree b){
return a.w < b.w;
}
ll Kruskal(){
ll ans = 0;
int num = 0;
for(int i = 1; i <= m; i++){
int u = Find(t[i].u);
int v = Find(t[i].v);
if(u != v){
pre[v] = u;
ans += t[i].w*1ll;
num++;
add(t[i].u, t[i].v, t[i].w);
add(t[i].v, t[i].u, t[i].w);
vis[i] = 1;
}
if(num == n-1)
break;
}
return num == n-1?ans:-1;
}
void dfs(int u, int d){
deep[u] = d;
for(int i = head[u]; ~i; i = e[i].next){
int v = e[i].v;
if(fa[v][0]) continue;
fa[v][0] = u;
dis1[v][0] = e[i].w;
dis2[v][0] = -inf;
dfs(v, d+1);
}
}
void update(){
for(int j = 1;j <= 20; j++){
for(int i = 1; i <= n; i++){
fa[i][j] = fa[fa[i][j-1]][j-1];
dis1[i][j] = max(dis1[i][j-1], dis1[fa[i][j-1]][j-1]);
if(dis1[i][j-1] == dis1[fa[i][j-1]][j-1])
dis2[i][j] = max(dis2[i][j-1], dis2[fa[i][j-1]][j-1]);
else if(dis1[i][j-1] < dis1[fa[i][j-1]][j-1])
dis2[i][j] = max(dis1[i][j-1], dis2[fa[i][j-1]][j-1]);
else
dis2[i][j] = max(dis2[i][j-1], dis1[fa[i][j-1]][j-1]);
}
}
}
int lca(int u, int v){
if(deep[u] < deep[v])
swap(u, v);
int f = deep[u] - deep[v];
for(int i = 0; (1<<i) <= f; i++){
if(f & (1<<i)){
u = fa[u][i];
}
}
if(u != v){
for(int i = 20; i >= 0; i--){
if(fa[u][i] != fa[v][i]){
u = fa[u][i];
v = fa[v][i];
}
}
u = fa[u][0];
}
return u;
}
int cal(int u, int f, int w){
int v1, v2;
v1 = v2 = -inf;
int len = deep[u] - deep[f];
for(int i = 0; (1<<i) <= len; i++){
if(len & (1<<i)){
v1 = max(dis1[u][i], v1);
if(dis1[u][i] != v1)
v2 = max(v2, dis1[u][i]);
v2 = max(v2, dis2[u][i]);
u = fa[u][i];
}
}
if(v1 == w) return w - v2;
else return w - v1;
}
int main(){
while(scanf("%d%d", &n, &m)!=EOF){
int u, v, w;
cnt = 0;
memset(fa, 0, sizeof(fa));
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++){
pre[i] = i;
}
for(int i = 1; i <= m; i++){
scanf("%d%d%d", &t[i].u, &t[i].v, &t[i].w);
}
sort(t+1, t+1+m, cmp);
ll sum = Kruskal();
fa[1][0] = 1;
dfs(1, 1);
update();
int Min = inf;
for(int i = 1; i <= m; i++){
if(!vis[i]){
int f = lca(t[i].u, t[i].v);
Min = min(cal(t[i].u, f, t[i].w), Min);
Min = min(cal(t[i].v, f, t[i].w), Min);
}
}
printf("%lld\n", sum*1ll + Min*1ll);
}
return 0;
}