[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把最小 ...
随机推荐
- Windows Server Backup 裸机恢复
1.打开“Windows Server Backup”选择本地备份,并在操作栏选择“一次性备份”:(在实际生产环境中可以根据自己的需求,选择一次性备份还是选择备份计划.) 2.打开“一次性备份向导”, ...
- 「题目代码」P1013~P1017(Java)
1013 C基础-求偶数和 import java.util.*; import java.io.*; import java.math.BigInteger; public class Main { ...
- ES6 中 export ,export default 区别
1.export与export default均可用于导出常量.函数.文件.模块等: 2.你可以在其它文件或模块中通过import+(常量 | 函数 | 文件 | 模块)名的方式,将其导入,以便能够对 ...
- C++学习008-delete与delete[]的差别
对于简单的数据类型,delete与delete[]是没啥差别的,就是等价的 例如 int main() { int *pdata = new int[20]; delete pdata; //dele ...
- C#下16进制和BCD码转换代码
private static Byte[] ConvertFrom(string strTemp) { try { if (Convert.ToBoolean(strTemp.Length & ...
- nodeJs 调试异步程序追踪异步报错
DeprecationWarning: Calling an asynchronous function without callback is deprecated. 翻译: 不建议在不回调的情况下 ...
- python 面试题: 列表表达式
[process() for item1 in iterable1 if condition1 for item2 in iterable2 if condition2 For item3 in it ...
- css字体图标的制作和使用。
css字体图标的制作和使用. 在项目开发的过程中,我们会经常用到一些图标.但是我们在使用这些图标时,往往会遇到失真的情况,而且图片数量很多的话,页面加载就越慢.所以,我们可以使用字体图标的方式来显示图 ...
- WCF分布式开发步步为赢(12):WCF事务机制(Transaction)和分布式事务编程
今天我们继续学习WCF分布式开发步步为赢系列的12节:WCF事务机制(Transaction)和分布式事务编程.众所周知,应用系统开发过程中,事务是一个重要的概念.它是保证数据与服务可靠性的重要机制. ...
- 注意@Bean中的initMethod和destroyMethod
@Configuration public class AppConfig { @Bean(initMethod = "init") public Foo foo() { retu ...