题意理解
要你构造一棵nn个节点的严格次小生成树.
算法解析
分析条件
题目中给出的关键点,就是严格和次小.
什么是严格
就是题目强制要求严格单调性,不可以有=号的出现.
什么是次小
我们应该都知道,最小生成树,它要求边集合的边总和最小,那么次小生成树,要求边集合的边总和只比最小生成树边集合权值大.
总结性质
有至少一个(严格)次小生成树,和最小生成树之间只有一条边的差异
总而言之,言而总之,我们现在知道了这条多余边的加入.,一定会产生非最小生成树.
我们不妨令
ans=最小生成树边权之和
假如说我们将多余边,替换掉最大权值边.
Val1==>z此时我们发现当前生成树
W=ans+z−Val1
W=最小生成边权之和+加上多余边−最大权值边
这一轮替换,我们可以认为这棵生成树有潜力成为次小生成树.
然后,我们发现,换一换次大边,也是可以的.
我们将多余边,强行替换掉次大权值边.
Val2==>z此时当前生成树
W=ans+z−Val2
W=最小生成树之和+加入多余边−次大权值边
现在所有的候选生成树都出来了,但是我们面临一个非常严重的问题.
我们如何快速计算,一条路径上的最大边,和次大边.
动态规划
我们可以当前需要知道的状态,无非就是两个.
一条路径上的最大边
一条路径上的严格次大边
所以说,我们不妨就按照倍增数组的思路,去制造两个新数组.
最大边数组
严格次大边数组
f[x][k]=f[fa[x][k−1]][k−1]
f[x][k]=f[fa[x][k−1]][k−1]
这是我们非常熟悉的Lca倍增数组.
然后咱们现在其实,手上掌握的最有力的性质,就是最值性质.
我们假设一条路径是由三段构造而成.
是三段,不是就三个点.
a=>c,c=>b,b=>a
a=>c,c=>b,b=>a

我们发现
A=>B的最大值其实等于max(A=>C最大值,B=>C最大值)
这就是区间最值性质.
不过严格次大边,就比较麻烦了,不慌,咱们慢慢画图来.
为了下面简述方面,我们设置一下变量.
A=>C上最大边权为ValA,C 次大边权为VA,C
C=>B上最大边权为ValB,C 次大边权为VB,C
A=>B上最大边权为ValA,B次大边权为VA,B
巧计一下,Val字母多,所以是最大边权,V字母少,所以是次大边权.
我们分类讨论一下,三种情况.
①第一段最大值=第二段最大值
ValA,C=ValB,C
我们发现两段居然最大值一样.
次大边权就只能
VA,B=max(VA,C,VB,C)
②第一段最大值<第二段最大值.
那么此时,次大边权是可以取第一段最大值.
因为此时总段的最大值,一定是第二段最大值.
ValA,B=ValB,C因此VA,B可以=ValA,C
综上所述,我们总结下来就是.
VA,B=max(ValA,C,VB,C)
③第一段最大值>第二段最大值.
那么此时,次大边权是可以取第二段最大值.
因为此时总段的最大值,一定是第一段最大值.
ValA,B=ValA,C因此VA,B可以=ValB,C
同样,总结一下.
VA,B=max(ValB,C,vA,B)
然后我们将A,B,C具体化一下.
A其实就是起始节点.
C其实就是A跳跃了2^(i−1)格节点.
B其实就是A跳跃了2^i格节点.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll inf=0x3f3f3f3f;
int n,m,fa[maxn],val1,val2,dp[3][maxn][20],t,head[maxn],f[maxn][20],deep[maxn];
ll ans,ans_max;
struct edge1 {
int v, next,w;
}e[maxn*2];
struct edge2 {
int u, v, w, vis;
bool operator<(const edge2 &b) const {
return w < b.w;
}
}a[maxn*3];
void add(int u,int v,int w) {
t++;
e[t].v = v;
e[t].w = w;
e[t].next = head[u];
head[u] = t;
}
int fin(int x){
return x==fa[x]?x:fa[x]=fin(fa[x]);
}
void Kruskal() {
sort(a + 1, a + m + 1);
for (int i = 1; i <= m; i++) {
int u = fin(a[i].u);
int v = fin(a[i].v);
if (u == v) continue;
fa[u] = v;
a[i].vis = 1;
ans += 1ll*a[i].w;
add(a[i].u, a[i].v, a[i].w);
add(a[i].v, a[i].u, a[i].w);
}
}
void dfs(int u,int fa) {
for (int i = 1; (1 << i) <= deep[u]; i++) {
f[u][i] = f[f[u][i - 1]][i - 1];
dp[0][u][i] = max(dp[0][u][i - 1], dp[0][f[u][i - 1]][i - 1]);
if (dp[0][u][i - 1] != dp[0][f[u][i - 1]][i - 1]) {
dp[1][u][i] = min(dp[0][u][i - 1], dp[0][f[u][i - 1]][i - 1]);
dp[1][u][i] = max(dp[1][u][i], dp[1][u][i - 1]);
dp[1][u][i] = max(dp[1][u][i], dp[1][f[u][i - 1]][i - 1]);
}
else
dp[1][u][i] = max(dp[1][u][i - 1], dp[1][f[u][i - 1]][i - 1]);
}
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if (v == fa) continue;
f[v][0] = u;
dp[0][v][0] = e[i].w;
dp[1][v][0] = -inf;
deep[v] = deep[u] + 1;
dfs(v, u);
}
}
void update2(int x) {
if (x > val1) {
val2 = val1;
val1 = x;
} else if (x > val2 && x != val1)
val2 = x;
}
void update(int x,int i) {
update2(dp[0][x][i]);
update2(dp[1][x][i]);
}
void lca(int x,int y) {
val1 = val2 = -inf;
if (deep[x] < deep[y]) {
swap(x, y);
}
int h = deep[x] - deep[y], k = 0;
while (h) {
if (h & 1) {
update(x,k);
x = f[x][k];
}
h >>= 1;
k++;
}
if (x == y) return;
for (int k = 19; k >= 0; k--) {
if (f[x][k] != f[y][k]) {
update(x, k);
x = f[x][k];
update(y, k);
y = f[y][k];
}
}
update(x,0);
update(y,0);
}
int main() {
scanf("%d%d", &n, &m);
for (int i=1;i<=n;i++){
fa[i]=i;
}
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &a[i].u, &a[i].v, &a[i].w);
}
Kruskal();
dfs(1, 0);
ans_max = 0x3f3f3f3f3f3f3f3f;
for (int i = 1; i <= m; i++) {
if (!a[i].vis) {
lca(a[i].u, a[i].v);
if (val1 != a[i].w)
ans_max = min(ans_max, ans - val1 + a[i].w);
else ans_max = min(ans_max, ans - val2 + a[i].w);
}
}
printf("%lld
", ans_max);
}