Brief Description

求一个无向图的严格次小生成树。

Algorithm Design

考察最小生成树的生成过程。对于一个非树边而言,如果我们使用这一条非树边去替换原MST的路径上的最大边,可以证明仍然满足生成树性质,而且这个生成树的大小一定不小于原生成树,那么枚举所有这样的非树边,尝试去替换,找到最小值就可以了。

那么问题就转化成了求树上两个点的最大/最小距离,这是树上倍增的经典应用,可以知道:

\[Max(x,i) = max(Max(x,i-1), Max(fa(x,i-1),i-1))
\]

我们可以通过递推用\(\Theta(nlogn)\)时间预处理出来这些信息。

问题解决了吗?还没有。原题要求严格小于原树。所以我们顺便预处理一下树上两个点的次大值就可以了。

Code

#include <algorithm>
#include <cstdio>
using std::max;
using std::min;
const int maxn = 100001;
const int maxm = 300001;
const int inf = 0x3f3f3f;
#define ll long long
int n, m, tot, cnt, mn = inf;
ll ans;
int f[maxn], head[maxn], deep[maxn], fa[maxn][17], d1[maxn][17], d2[maxn][17];
struct data {
int x, y, v;
bool sel;
bool operator<(const data b) const { return this->v < b.v; }
} a[maxm];
struct edge {
int to, next, v;
} e[maxn << 1];
void ins(int u, int v, int w) {
e[++cnt].to = v;
e[cnt].next = head[u];
e[cnt].v = w;
head[u] = cnt;
}
int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
void dfs(int x, int f) {
for (int i = 1; i <= 16; i++) {
if (deep[x] < (1 << i))
break;
fa[x][i] = fa[fa[x][i - 1]][i - 1];
d1[x][i] = max(d1[x][i - 1], d1[fa[x][i - 1]][i - 1]);
if (d1[x][i - 1] == d1[fa[x][i - 1]][i - 1])
d2[x][i] = max(d2[x][i - 1], d2[fa[x][i - 1]][i - 1]);
else {
d2[x][i] = min(d1[x][i - 1], d1[fa[x][i - 1]][i - 1]);
d2[x][i] = max(d2[x][i], d2[x][i - 1]);
d2[x][i] = max(d2[x][i], d2[fa[x][i - 1]][i - 1]);
}
}
for (int i = head[x]; i; i = e[i].next) {
if (e[i].to != f) {
fa[e[i].to][0] = x;
d1[e[i].to][0] = e[i].v;
deep[e[i].to] = deep[x] + 1;
dfs(e[i].to, x);
}
}
}
int lca(int x, int y) {
if (deep[x] < deep[y])
std::swap(x, y);
int t = deep[x] - deep[y];
for (int i = 0; i <= 16; i++)
if (t & (1 << i))
x = fa[x][i];
for (int i = 16; i >= 0; i--)
if (fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
if (x == y)
return x;
return fa[x][0];
}
void cal(int x, int f, int v) {
int mx1 = 0, mx2 = 0;
int t = deep[x] - deep[f];
for (int i = 0; i <= 16; i++) {
if (t & (1 << i)) {
if (d1[x][i] > mx1) {
mx2 = mx1;
mx1 = d1[x][i];
}
mx2 = max(mx2, d2[x][i]);
x = fa[x][i];
}
}
if (mx1 != v)
mn = min(mn, v - mx1);
else
mn = min(mn, v - mx2);
}
void solve(int t, int v) {
int x = a[t].x, y = a[t].y, f = lca(x, y);
cal(x, f, v);
cal(y, f, v);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input", "r", stdin);
#endif
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++)
f[i] = i;
for (int i = 1; i <= m; i++)
scanf("%d %d %d", &a[i].x, &a[i].y, &a[i].v);
std::sort(a + 1, a + m + 1);
for (int i = 1; i <= m; i++) {
int p = find(a[i].x), q = find(a[i].y);
if (p != q) {
f[p] = q;
ans += a[i].v;
a[i].sel = 1;
ins(a[i].x, a[i].y, a[i].v);
ins(a[i].y, a[i].x, a[i].v);
tot++;
if (tot == n - 1)
break;
}
}
dfs(1, 0);
for (int i = 1; i <= m; i++)
if (!a[i].sel)
solve(i, a[i].v);
printf("%lld\n", ans + mn);
}

[bzoj1977][BeiJing2010组队]次小生成树 Tree——树上倍增+lca的更多相关文章

  1. 【BZOJ1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+倍增

    [BZOJ1977][BeiJing2010组队]次小生成树 Tree Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C ...

  2. 【次小生成树】bzoj1977 [BeiJing2010组队]次小生成树 Tree

    Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一 ...

  3. bzoj1977 [BeiJing2010组队]次小生成树 Tree

    和倍增法求lca差不多,维护每个点往上跳2^i步能到达的点,以及之间的边的最大值和次大值,先求出最小生成树,对于每个非树边枚举其端点在树上的路径的最大值,如果最大值和非树边权值一样则找次大值,然后维护 ...

  4. bzoj1977 [BeiJing2010组队]次小生成树 Tree——严格次小生成树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1977 因为严格,所以要记录到 LCA 的一个次小值: 很快写好,然后改掉一堆错误后终于过了样 ...

  5. [BeiJing2010组队]次小生成树 Tree

    1977: [BeiJing2010组队]次小生成树 Tree Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 5168  Solved: 1668[S ...

  6. BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )

    做一次MST, 枚举不在最小生成树上的每一条边(u,v), 然后加上这条边, 删掉(u,v)上的最大边(或严格次大边), 更新答案. 树链剖分然后ST维护最大值和严格次大值..倍增也是可以的... - ...

  7. 1977: [BeiJing2010组队]次小生成树 Tree

    1977: [BeiJing2010组队]次小生成树 Tree https://lydsy.com/JudgeOnline/problem.php?id=1977 题意: 求严格次小生成树,即边权和不 ...

  8. 【bzoj1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+权值线段树合并

    题目描述 求一张图的严格次小生成树的边权和,保证存在. 输入 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z ...

  9. 洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

    洛谷题目传送门 %%%TPLY巨佬和ysner巨佬%%% 他们的题解 思路分析 具体思路都在各位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 大多数算法都要用kruskal把最小 ...

随机推荐

  1. 深入浅出 Webpack

    深入浅出 Webpack 评价 Webpack 凭借强大的功能与良好的使用体验,已经成为目前最流行,社区最活跃的打包工具,是现代 Web 开发必须掌握的技能之一.作者结合自身的实战经验,介绍了 Web ...

  2. web开发微信文章目录

    Web开发微信文章目录 2015-12-13 Web开发 本文是Web开发微信的文章目录.通过目录查看文章编号,回复文章编号就能查看文章全文. 回复编号查看全文,搜索分类名可以获得该分类下的文章.   ...

  3. Hackerrank - [Algo] Matrix Rotation

    https://www.hackerrank.com/challenges/matrix-rotation-algo 又是一道耗了两小时以上的题,做完了才想起来,这不就是几年前在POJ上做过的一个同类 ...

  4. Android之线程安全的单例模式,Adapter注意事项之引用传值

    线程安全的单例模式单位模式一般写法如下: public static FestivalLab mInstance; private FestivalLab() { } public static Fe ...

  5. ES5新增数组方法(4):every

    检查数组元素的每个元素是否符合条件. // 数组中的元素全部满足指定条件返回true let arr = [1, 3, 5, 7, 9]; console.log(arr.every((value, ...

  6. 算法搬运之BFPRT算法

    原文连接:http://noalgo.info/466.html BFPRT算法,又称为中位数的中位数算法,由5位大牛(Blum . Floyd . Pratt . Rivest . Tarjan)提 ...

  7. 数据库学习(二) case when then else end 的使用

    case函数还用来统计数据的,参考资料:https://www.cnblogs.com/qlqwjy/p/7476533.html 这里我只是整理下工作中使用的到案例: 查询语句: SELECT t. ...

  8. (转)Unreal Networking Guide Created by Zach Metcalf

        2. 3.                                                        

  9. 1066 Root of AVL Tree (25 分)(平衡二叉树)

    就是AVL的模板题了 注意细节 #include<bits/stdc++.h> using namespace std; typedef struct node; typedef node ...

  10. day-10 sklearn库实现SVM支持向量算法

    学习了SVM分类器的简单原理,并调用sklearn库,对40个线性可分点进行训练,并绘制出图形画界面. 一.问题引入 如下图所示,在x,y坐标轴上,我们绘制3个点A(1,1),B(2,0),C(2,3 ...