题解【LOJ10068】「一本通 3.1 练习 3」秘密的牛奶运输
题目就是要我们求出图中严格次小生成树的边权之和。
我们首先可以发现一个很显然的性质:严格次小生成树与最小生成树有且只有一条边不同。
我们先求出图的最小生成树,然后枚举每一条非树边,尝试用它来替换树上的某一条边,使得我们新得到的树是图的严格次小生成树。
具体地说,我们设当前枚举到的非树边的两个端点分别是 \(u\) 和 \(v\),它们之间的距离是 \(w\),最小生成树的边权总和是 \(sum\)。
那么我们的答案就是 \(\min\{sum-w+dist_{u,v}\}\)。其中 \(dist_{u,v}\) 是 \(u\) 到 \(v\) 在最小生成树上最大的边权。
这个可以通过 DFS 预处理得到。
注意在求答案时一定要先判断 \(w\) 是否大于 \(dist_{u,v}\)。
我们注意到还有一个问题:如果 \(u\) 到 \(v\) 的最大边权与 \(w\) 相等时怎么办?
其实这个问题也不能难解决:我们只要再预处理一下 \(u\) 到 \(v\) 的次大边权,转移时再判断 \(w\) 是否大于次大边权即可。
记得开 long long。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 503, M = 20003;
int n, m;
struct Node
{
int u, v, w;
bool fl; //fl 为 true 表示是树边,否则就是非树边
} a[M];
int tot, head[N], ver[M], nxt[M], edge[M];
int dist[N][N], dist1[N][N]; //两点之间的最大距离和次大距离
int fa[N];
LL res, sum, ans;
inline void add(int u, int v, int w)
{
ver[++tot] = v, edge[tot] = w, nxt[tot] = head[u], head[u] = tot;
}
int getf(int u)
{
if (fa[u] == u) return u;
return fa[u] = getf(fa[u]);
}
inline bool cmp(Node x, Node y)
{
return x.w < y.w;
}
void dfs(int rt, int u, int f, int maxd, int max2d)
{
dist[rt][u] = maxd, dist1[rt][u] = max2d;
for (int i = head[u]; i; i = nxt[i])
{
int v = ver[i], w = edge[i];
if (v == f) continue;
int maxxd = maxd, maxx2d = max2d;
if (w > maxxd) maxxd = w; //更新最大值
else if (w < maxxd && w > maxx2d) maxx2d = w; //一定要注意判断 w 是否小于最大值后再更新次大值
dfs(rt, v, u, maxxd, maxx2d);
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i+=1)
{
cin >> a[i].u >> a[i].v >> a[i].w;
}
sort(a + 1, a + 1 + m, cmp);
for (int i = 1; i <= n; i+=1) fa[i] = i;
for (int i = 1; i <= m; i+=1)
{
int u = getf(a[i].u), v = getf(a[i].v);
if (u != v)
{
fa[u] = v;
sum += a[i].w;
add(a[i].u, a[i].v, a[i].w);
add(a[i].v, a[i].u, a[i].w); //建立最小生成树
a[i].fl = true;
}
}
for (int i = 1; i <= n; i+=1) dfs(i, i, -1, 0, 0);
ans = 1000000000000000000;
for (int i = 1; i <= m; i+=1)
if (!a[i].fl)
{
int u = a[i].u, v = a[i].v;
if (a[i].w > dist[u][v]) //大于最大边权
ans = min(ans, sum - dist[u][v] + a[i].w);
else if (a[i].w > dist1[u][v]) //大于次大边权
ans = min(ans, sum - dist1[u][v] + a[i].w);
}
cout << ans << endl;
return 0;
}