次小生成树
题意理解
要你构造一棵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\n", ans_max); }