[bzoj1977][BeiJing2010组队]次小生成树 Tree——树上倍增+lca
Brief Description
求一个无向图的严格次小生成树。
Algorithm Design
考察最小生成树的生成过程。对于一个非树边而言,如果我们使用这一条非树边去替换原MST的路径上的最大边,可以证明仍然满足生成树性质,而且这个生成树的大小一定不小于原生成树,那么枚举所有这样的非树边,尝试去替换,找到最小值就可以了。
那么问题就转化成了求树上两个点的最大/最小距离,这是树上倍增的经典应用,可以知道:
\]
我们可以通过递推用\(\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的更多相关文章
- 【BZOJ1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+倍增
[BZOJ1977][BeiJing2010组队]次小生成树 Tree Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C ...
- 【次小生成树】bzoj1977 [BeiJing2010组队]次小生成树 Tree
Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一 ...
- bzoj1977 [BeiJing2010组队]次小生成树 Tree
和倍增法求lca差不多,维护每个点往上跳2^i步能到达的点,以及之间的边的最大值和次大值,先求出最小生成树,对于每个非树边枚举其端点在树上的路径的最大值,如果最大值和非树边权值一样则找次大值,然后维护 ...
- bzoj1977 [BeiJing2010组队]次小生成树 Tree——严格次小生成树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1977 因为严格,所以要记录到 LCA 的一个次小值: 很快写好,然后改掉一堆错误后终于过了样 ...
- [BeiJing2010组队]次小生成树 Tree
1977: [BeiJing2010组队]次小生成树 Tree Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 5168 Solved: 1668[S ...
- BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )
做一次MST, 枚举不在最小生成树上的每一条边(u,v), 然后加上这条边, 删掉(u,v)上的最大边(或严格次大边), 更新答案. 树链剖分然后ST维护最大值和严格次大值..倍增也是可以的... - ...
- 1977: [BeiJing2010组队]次小生成树 Tree
1977: [BeiJing2010组队]次小生成树 Tree https://lydsy.com/JudgeOnline/problem.php?id=1977 题意: 求严格次小生成树,即边权和不 ...
- 【bzoj1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+权值线段树合并
题目描述 求一张图的严格次小生成树的边权和,保证存在. 输入 第一行包含两个整数N 和M,表示无向图的点数与边数. 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z ...
- 洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)
洛谷题目传送门 %%%TPLY巨佬和ysner巨佬%%% 他们的题解 思路分析 具体思路都在各位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 大多数算法都要用kruskal把最小 ...
随机推荐
- 关闭 Identity 插入限制
当为identity列插入时会报错: 仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表'xx'中的标识列指定显式值. 但在复制表数据时想带主键一起复制时,这时要设置IDEN ...
- 【廖雪峰老师python教程】——模块
使用模块 任何模块代码的第一个字符串都被视为模块的文档注释: 使用__author__变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名: 当我们在命令行运行模块文件时,Python解释器把 ...
- 在PXC中重新添加掉线节点
Preface When we add a new node into PXC structure,it will estimate the mothed(IST/SST) to tr ...
- BZOJ 1968 [Ahoi2005]COMMON 约数研究:数学【思维题】
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1968 题意: 设f(x) = x约数的个数.如:12的约数有1,2,3,4,6,12,所以 ...
- 【转】Linux学习(1)-常用快捷键、文件管理和查询
原文链接:http://www.cnblogs.com/zhaopei/p/7397402.html 有话要说 为什么要用Linux?要用Linux的原因太多,想说说不完啊. 如果你说用Linux只是 ...
- Gitlab自动触发Jenkins构建项目
Gitlab自动触发Jenkins构建项目 一.前提 Gitlab已安装配置好. Jenkins已安装Gitlab plugin. 二.配置jenkins中Job 1.勾选触发器下的gitlab触发器 ...
- PAT java大数 A+B和C
题目描述: 给定区间[-, ]内的3个整数A.B和C,请判断A+B是否大于C. 输入格式: 输入第1行给出正整数T(<=),是测试用例的个数.随后给出T组测试用例,每组占一行,顺序给出A.B和C ...
- [剑指Offer] 30.连续子数组的最大和
题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学.今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决.但是,如果向量 ...
- zTree删除节点
zTree 是一个依靠 jQuery 实现的多功能 “树插件”.优异的性能.灵活的配置.多种功能的组合是 zTree 最大优点. zTree删除节点. <!DOCTYPE html> &l ...
- IHE PIX规范
IHE(Integrating Healthcare Enterprise) 集成医疗企业 IHE概念是由医学专家和广大医护工作者.相关政府部门.信息技术专家和企业共同发起的,目的是提供一种更好的方法 ...