【BJWC2010】次小生成树
题目链接:https://www.luogu.com.cn/problem/P4180
题目大意:给定一张含有 \(m\) 条边的无向带权图 , 求出这张图中边权之和严格大于最小生成树的次小生成树的边权之和
solution
笔者太鸽了 , 一连咕了三天 , 因此来补一下前两天的锅
这道题的思路很显然 , 先求出这张图的最小生成树 , 记它的边权之和为\(sum\) , 再考虑剩下的 \(m - n + 1\) 条边 , 如果把第 \(i\) 条, 分别连接\(a_i , b_i\), 权值为\(w_i\)的边加入树中 , 显然组成的生成树的最小值为 \(sum - max\left\{dist(u, v)\right\} + w_i\) (其中 \(u\) , \(v\) 为 \(path( a_i , b_i )\) 上的点) , 但要注意 , 这道题要求的是严格次小生成树 , 如果 \(max\left\{dist(u, v)\right\} = w_i\) 的话 , 求出的还是最小生成树 , 对于这种情况 , 还需要求出 \(max_{second}\left\{dist(u, v)\right\}\) , 组成的生成树最小值为 \(sum - max_{second}\left\{dist(u, v)\right\} + w_i\) , 最后比较每条边组成的生成树大小 , 取其中的最小值即可
对于求 \(max\left\{dist(u, v)\right\} = w_i\) 以及 \(max_{second}\left\{dist(u, v)\right\}\) 的值 , 可以分别用倍增处理 , 十分套路 , 笔者就不加赘述了
时间复杂度 : \(O(mlogn)\)
code
#include<bits/stdc++.h>
using namespace std;
template <typename T> inline void read(T &FF) {
int RR = 1; FF = 0; char CH = getchar();
for(; !isdigit(CH); CH = getchar()) if(CH == '-') RR = -RR;
for(; isdigit(CH); CH = getchar()) FF = FF * 10 + CH - 48;
FF *= RR;
}
inline void file(string str) {
freopen((str + ".in").c_str(), "r", stdin);
freopen((str + ".out").c_str(), "w", stdout);
}
const int N = 1e5 + 10, Log = 21, M = 3e5 + 10;
int n, m, mf[N], fa[N][Log + 1];
long long g[N][Log + 1][2], wi[N << 1], ans = LONG_LONG_MAX, sum;
int now, fst[N], nxt[N << 1], num[N << 1], dep[N], fl[M];
struct edge{
int u, v, w;
friend bool operator < (edge ai, edge bi) {
return ai.w < bi.w;
}
}path[M];
int get_fa(int xi) {
return mf[xi] == xi ? xi : mf[xi] = get_fa(mf[xi]);
}
void add(int u, int v, int w) {
nxt[++now] = fst[u], fst[u] = now, num[now] = v, wi[now] = w;
nxt[++now] = fst[v], fst[v] = now, num[now] = u, wi[now] = w;
}
void pre_bz(int xi) {
dep[xi] = dep[fa[xi][0]] + 1;
for(int i = 1; i <= Log; i++) {
fa[xi][i] = fa[fa[xi][i - 1]][i - 1];
g[xi][i][0] = max(g[xi][i - 1][0], g[fa[xi][i - 1]][i - 1][0]);
if(g[xi][i - 1][0] == g[fa[xi][i - 1]][i - 1][0])
g[xi][i][1] = max(g[xi][i - 1][1], g[fa[xi][i - 1]][i - 1][1]);
else if(g[xi][i - 1][0] > g[fa[xi][i - 1]][i - 1][0])
g[xi][i][1] = max(g[xi][i - 1][1], g[fa[xi][i - 1]][i - 1][0]);
else g[xi][i][1] = max(g[xi][i - 1][0], g[fa[xi][i - 1]][i - 1][1]);
}
for(int i = fst[xi]; i; i = nxt[i])
if(num[i] != fa[xi][0]) {
fa[num[i]][0] = xi;
g[num[i]][0][0] = wi[i], g[num[i]][0][1] = LONG_LONG_MIN;
pre_bz(num[i]);
}
}
pair<long long, long long> fmax(int xi, int yi) {
pair<long long, long long> mans;
if(dep[xi] < dep[yi]) swap(xi, yi);
for(int i = Log; i >= 0; i--)
if(dep[fa[xi][i]] >= dep[yi]) {
if(mans.first < g[xi][i][0]) {
mans.second = max(mans.first, g[xi][i][1]);
mans.first = g[xi][i][0];
}
else if(mans.first > g[xi][i][0])
mans.second = max(mans.second, g[xi][i][0]);
else mans.second = max(mans.second, g[xi][i][1]);
xi = fa[xi][i]; //把往上跳这一步给漏了都有60pts
}
if(xi == yi) return mans;
for(int i = Log; i >= 0; i--)
if(fa[xi][i] != fa[yi][i]) {
if(mans.first < g[xi][i][0]) {
mans.second = max(mans.first, g[xi][i][1]);
mans.first = g[xi][i][0];
}
else if(mans.first > g[xi][i][0])
mans.second = max(mans.second, g[xi][i][0]);
else mans.second = max(mans.second, g[xi][i][1]);
if(mans.first < g[yi][i][0]) {
mans.second = max(mans.first, g[yi][i][1]);
mans.first = g[yi][i][0];
}
else if(mans.first > g[yi][i][0])
mans.second = max(mans.second, g[yi][i][0]);
else mans.second = max(mans.second, g[yi][i][1]);
xi = fa[xi][i], yi = fa[yi][i]; //这一步也是
}
if(g[xi][0][0] > mans.first)
mans.second = mans.first, mans.first = g[xi][0][0];
else if(g[xi][0][0] < mans.first)
mans.second = max(mans.second, g[xi][0][0]);
if(g[yi][0][0] > mans.first)
mans.second = mans.first, mans.first = g[yi][0][0];
else if(g[yi][0][0] < mans.first)
mans.second = max(mans.second, g[yi][0][0]);
return mans;
}
int main() {
//file("");
read(n), read(m);
for(int i = 1; i <= m; i++)
read(path[i].u), read(path[i].v), read(path[i].w);
sort(path + 1, path + m + 1);
for(int i = 1; i <= n; i++) mf[i] = i;
for(int i = 1; i <= m; i++) {
if(get_fa(path[i].v) == get_fa(path[i].u)) {
fl[i] = true;
continue;
}
sum += path[i].w;
add(path[i].u, path[i].v, path[i].w);
mf[get_fa(path[i].v)] = get_fa(path[i].u);
}
g[1][0][1] = LONG_LONG_MIN;
pre_bz(1);
for(int i = 1; i <= m; i++)
if(fl[i]) {
pair<long long, long long> res = fmax(path[i].u, path[i].v);
if(path[i].w > res.first) ans = min(ans, sum - res.first + path[i].w);
else ans = min(ans, sum - res.second + path[i].w);
}
cout << ans << endl;
return 0;
}