- 题意就是求严格次小生成树。
- 其实思路挺明确的,跟非严格次小生成树差不多。
- 那么先回忆一下非严格次小生成树:先求出最小生成树,再利用Kruskal的贪心思路,对每一条非树边求它连成的环上最大的一条边,用这条非树边替换掉,对于替换掉每一条边后的树权值取min就是答案。
- 严格与非严格的区别:不能相等了。所以好像只求最大值会出问题,如果一个环上有两条一样长的边,得到的结果就是非严格的。所以改进算法,倍增时记录权值的最大值和次大值,保证最大严格大于次大。然后几乎用相同的打法就打出来了
(然而WA了很久)
代码(打了注释):
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#define int long long
using namespace std;
const int INF = 2e18+10;
const int N = 1e5+10;
const int M = 4e5+10;
const int E = 20;
int n, m, sum;
struct PREEDGE{//pre_edge是图上的边
int u, v, w;
bool on_tree;
//重载小于号要打
bool operator < (const PREEDGE &x)const{return w < x.w;}
}pre_edge[M];
int fa[N];
struct TREEEDGE{//最小生成树的树边
int nxt, v, w;
}tree_edge[N*2];
int point[N], e;
int dpt[N], f[N][E+1], fst[N][E+1], scd[N][E+1], ans;
void add_edge(int u, int v, int w)
{
tree_edge[++e] = (TREEEDGE){point[u], v, w};
point[u] = e;
}
int FIND(int u)
{
if (fa[u] == u) return u;
return fa[u] = FIND(fa[u]);
}
bool cmp(int x, int y){return x > y;}
inline int max(int x, int y){return x > y ? x : y;}
inline int min(int x, int y){return x < y ? x : y;}
int SCD(int x, int y, int z, int w)//取四个数的次大值
{
int a[4] = {x, y ,z, w}, fstmax = -INF, scdmax = -INF;
for (int i = 0; i < 4; i++)
fstmax = max(fstmax, a[i]);
for (int i = 0; i < 4; i++)
if (a[i] != fstmax)
scdmax = max(scdmax, a[i]);
return scdmax;
}
void DFS(int u, int fa)
{
dpt[u] = dpt[fa]+1;
for (int i = 1; i <= E; i++){
f[u][i] = f[f[u][i-1]][i-1];
fst[u][i] = max(fst[u][i-1], fst[f[u][i-1]][i-1]);
scd[u][i] = SCD(fst[u][i-1], fst[f[u][i-1]][i-1], scd[u][i-1], scd[f[u][i-1]][i-1]);
}
for (int i = point[u]; i != -1; i = tree_edge[i].nxt){
int v = tree_edge[i].v;
int w = tree_edge[i].w;
if (v == fa) continue;
f[v][0] = u;//一开始这句话忘记打,然而过了洛谷的样例,懵逼提交了好几次
fst[v][0] = w;
DFS(v, u);
}
}
void UPDATE(int &x, int &y, int z)//更新最大值和次大值
{
if (z > x) y = x, x = z;
else if (z != x && z > y) y = z;
}
void LCA(int x, int y, int &fstmax, int &scdmax)
{
//注释掉的是原来的思路,开一个vector,把所有路上的最大值和次大值丢进去,sort加unique,然而本来我们只需要记录两个值,这样做浪费了很多时间
// vector<int> aa;
// aa.clear();
fstmax = scdmax = -INF;
if (dpt[x] < dpt[y]) swap(x, y);
for (int i = E; i >= 0; i--)
if (dpt[x]-(1<<i) >= dpt[y]){
UPDATE(fstmax, scdmax, fst[x][i]);
UPDATE(fstmax, scdmax, scd[x][i]);
x = f[x][i];
}
for (int i = E; i >= 0; i--)
if (f[x][i] != f[y][i]){
UPDATE(fstmax, scdmax, fst[x][i]);
UPDATE(fstmax, scdmax, scd[x][i]);
UPDATE(fstmax, scdmax, fst[y][i]);
UPDATE(fstmax, scdmax, scd[y][i]);
x = f[x][i];
y = f[y][i];
}
if (x != y){
UPDATE(fstmax, scdmax, fst[x][0]);
UPDATE(fstmax, scdmax, scd[x][0]);
UPDATE(fstmax, scdmax, fst[y][0]);
UPDATE(fstmax, scdmax, scd[y][0]);
}
// sort(aa.begin(), aa.end(), cmp);
// unique(aa.begin(), aa.end());
}
main()
{
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= m; i++){
scanf("%lld%lld%lld", &pre_edge[i].u, &pre_edge[i].v, &pre_edge[i].w);
pre_edge[i].on_tree = 0;
}
sort(pre_edge+1, pre_edge+m+1);
sum = 0;
for (int i = 1; i <= n; i++)
fa[i] = i;
memset(point, -1, sizeof(point)); e = 0;
for (int i = 1; i <= m; i++){
int u = pre_edge[i].u;
int v = pre_edge[i].v;
int w = pre_edge[i].w;
int fau = FIND(u);
int fav = FIND(v);
if (fau != fav){
fa[fau] = fav;
sum += w;
pre_edge[i].on_tree = 1;
add_edge(u, v, w);
add_edge(v, u, w);
}
}
memset(fst, ~0x3f, sizeof(fst));
memset(scd, ~0x3f, sizeof(scd));
memset(f, 0, sizeof(f)); dpt[0] = 0;
DFS(1, 0);
ans = INF;
for (int i = 1; i <= m; i++)
if (!pre_edge[i].on_tree){
int u = pre_edge[i].u;
int v = pre_edge[i].v;
int w = pre_edge[i].w;
int fstmax, scdmax;
LCA(u, v, fstmax, scdmax);
if (w != fstmax) ans = min(ans, w-fstmax);
else ans = min(ans, w-scdmax);
}
printf("%lld", ans+sum);
return 0;
}