kruskal 重构树(讲解 + 例题)

kruskal重构树

如何建树

模仿kruskalkruskalkruskal,先将所有边排序。

依次遍历每一条边,如果这条边的两个节点(u,vu, vu,v)不在同一个连通块里面,

则新建一个nodenodenode节点,更新fa[u]=fa[v]=nodefa[u] = fa[v] = nodefa[u]=fa[v]=node,同时有fa[node]=nodefa[node] = nodefa[node]=node

建立node−>u,node−>vnode-> u, node-> vnode>u,node>v的边,同时给nodenodenode节点赋值上这条边的边权,

一些性质

① 是一个小/大根堆(由建树时边权的排序方式决定)。

LCA(u,v)LCA(u, v)LCA(u,v)的权值是从uuuvvv的路径上的最大/最小边权的最小/最大值(同样由建树是边权的排序所决定)。

显然,对于每个新建的nodenodenode节点,一定有权值是由深度更小到深度更大递减的(如果排序时按照升序排列),反之亦然,所以易证得 ① ② 成立。

#3732. Network

#include <bits/stdc++.h>using namespace std;const int N = 1e5 + 10;int value[N], ff[N], nn, n, m, q;int head[N], to[N], nex[N], cnt = 1;int fa[N], son[N], sz[N], dep[N], top[N], tot;struct Res {int u, v, w;void read() {scanf("%d %d %d", &u, &v, &w);}bool operator < (const Res &t) const {return w < t.w;}
}a[N];void add(int x, int y) {to[cnt] = y;nex[cnt] = head[x];head[x] = cnt++;
}int find(int rt) {return rt == ff[rt] ? rt : ff[rt] = find(ff[rt]);
}void dfs1(int rt, int f) {fa[rt] = f, dep[rt] = dep[f] + 1, sz[rt] = 1;for (int i = head[rt]; i; i = nex[i]) {if (to[i] == f) {continue;}dfs1(to[i], rt);sz[rt] += sz[to[i]];if (sz[son[rt]] < sz[to[i]]) {son[rt] = to[i];}}
}void dfs2(int rt, int tp) {top[rt] = tp;if (!son[rt]) {return ;}dfs2(son[rt], tp);for (int i = head[rt]; i; i = nex[i]) {if (to[i] == fa[rt] || to[i] == son[rt]) {continue;}dfs2(to[i], to[i]);}
}int lca(int x, int y) {while (top[x] != top[y]) {if (dep[top[x]] < dep[top[y]]) {swap(x, y);}x = fa[top[x]];}return dep[x] < dep[y] ? x : y;
}void kruskal() {for (int i = 1; i < N; i++) {ff[i] = i;}for (int i = 1; i <= m; i++) {int u = a[i].u, v = a[i].v;int fu = find(u), fv = find(v);if (fu != fv) {value[++n] = a[i].w;ff[n] = ff[fu] = ff[fv] = n;add(n, fv), add(n, fu);}}dfs1(n, 0), dfs2(n, n);
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);scanf("%d %d %d", &nn, &m, &q), n = nn;for (int i = 1; i <= m; i++) {a[i].read();}sort(a + 1, a + 1 + m);kruskal();while (q--) {int u, v;scanf("%d %d", &u, &v);printf("%d\n", value[lca(u, v)]);}return 0;
}

#3551. [ONTAK2010]Peaks加强版

我们要求从一个点出发经过困难值小于等于xxx的路径所能到达的山峰中第kkk高的是什么。

考虑按照边权升序,建议kruskalkruskalkruskal重构树,然后倍增向上跳,找到困难值小于等于xxx的深度最小的节点uuu

那么我们只要在uuu的子树中询问第kkk大即可,所以可以用主席树来写,依照dfsdfsdfs序,对每个节点建立一颗主席树,然后在主席树上查找第kkk大即可。

#include <bits/stdc++.h>using namespace std;const int N = 5e5 + 10;int head[N], to[N], nex[N], cnt = 1;int n, m, q, nn, a[N], ff[N], value[N], h[N];int fa[N][21], l[N], r[N], rk[N], tot;int root[N], ls[N << 7], rs[N << 7], sum[N << 7], num;struct Res {int u, v, w;bool operator < (const Res &t) const {return w < t.w;}
}edge[N];void add(int x, int y) {to[cnt] = y;nex[cnt] = head[x];head[x] = cnt++;
}void update(int &rt, int pre, int l, int r, int x, int v) {rt = ++num;ls[rt] = ls[pre], rs[rt] = rs[pre], sum[rt] = sum[pre] + v;if (l == r) {return ;}int mid = l + r >> 1;if (x <= mid) {update(ls[rt], ls[pre], l, mid, x, v);}else {update(rs[rt], rs[pre], mid + 1, r, x, v);}
}int query(int L, int R, int l, int r, int k) {if (l == r) {return l;}int res = sum[ls[R]] - sum[ls[L]], mid = l + r >> 1;if (res >= k) {return query(ls[L], ls[R], l, mid, k);}else {return query(rs[L], rs[R], mid + 1, r, k - res);}
}int find(int rt) {return rt == ff[rt] ? rt : ff[rt] = find(ff[rt]);
}void dfs(int rt, int f) {fa[rt][0] = f, l[rt] = ++tot, rk[tot] = rt;for (int i = 1; i <= 20; i++) {fa[rt][i] = fa[fa[rt][i - 1]][i - 1];}for (int i = head[rt]; i; i = nex[i]) {if (to[i] == f) {continue;}dfs(to[i], rt);}r[rt] = tot;
}void kruskal() {for (int i = 1; i < N; i++) {ff[i] = i;}sort(edge + 1, edge + 1 + m);for (int i = 1, cur = 1; i <= m && cur < n; i++) {int u = find(edge[i].u), v = find(edge[i].v);if (u != v) {cur++, nn++;ff[u] = nn, ff[v] = nn;value[nn] = edge[i].w;add(nn, u), add(nn, v);if (u <= n) {value[u] = edge[i].w;}if (v <= n) {value[v] = edge[i].w;}}}dfs(nn, 0);
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);scanf("%d %d %d", &n, &m, &q);for (int i = 1; i <= n; i++) {scanf("%d", &h[i]);a[i] = h[i];}nn = n;for (int i = 1; i <= m; i++) {scanf("%d %d %d", &edge[i].u, &edge[i].v, &edge[i].w);}kruskal();int maxn = n;sort(a + 1, a + 1 + maxn);maxn = unique(a + 1, a + 1 + maxn) - (a + 1);for (int i = 1; i <= n; i++) {h[i] = lower_bound(a + 1, a + 1 + maxn, h[i]) - a; }for (int i = 1; i <= nn; i++) {root[i] = root[i - 1];if (rk[i] <= n) {update(root[i], root[i], 1, maxn, h[rk[i]], 1);}}for (int i = 1, u, x, k, last_ans = 0, res; i <= q; i++) {scanf("%d %d %d", &u, &x, &k);if (last_ans != -1) {u ^= last_ans, x ^= last_ans, k ^= last_ans;}for (int j = 20; j >= 0; j--) {if (fa[u][j] && value[fa[u][j]] <= x) {u = fa[u][j];}}res = sum[root[r[u]]] - sum[root[l[u] - 1]];last_ans = res < k ? -1 : a[query(root[l[u] - 1], root[r[u]], 1, maxn, res - k + 1)];printf("%d\n", last_ans);}return 0;
}

P4768 [NOI2018] 归程

给定一个nnn个点,mmm条边的无向联通图,边的描述为[u,v,l,a][u, v, l, a][u,v,l,a],表示uuuvvv连有一条长度为lll,海拔为aaa的边,

QQQ个询问,每次给出一个出发点uuu和一个海拔限制高度ppp,并且在出发点有一辆车,这辆车可以通过海拔大于ppp的边,

问,从u−>1u->1u>1的最短步行长度是什么多少。

设从uuu坐车出发可到的点集为SSS,我们的任务就是找到一个点v,v∈Sv, v \in Sv,vSdis(v,1)dis(v, 1)dis(v,1)dis(x,1),x∈Sdis(x, 1),x \in Sdis(x,1),xS中最的小。

① 预处理出每个点到点111的最短路径出来,

② 我们按照海拔高度降序建立一颗kruskalkruskalkruskal重构树,

③ 从uuu号点往上跳,找到可坐车到达的深度最小的节点rtrtrt,显然从uuu可坐车到达的点集就是rtrtrt所在的这颗子树,

④ 由于我们查找的是最小值,所以只需在dfsdfsdfs的过程中,不断向上更新整颗子树的最小值即可。

⑤ 直接输出我们找到的rtrtrt所代表的答案。

#include <bits/stdc++.h>using namespace std;const int N = 1e6 + 10;int head[N], to[N], nex[N], cnt = 1;int head1[N], to1[N], nex1[N], value1[N], cnt1 = 1;int vis[N], dis[N], ff[N], value[N], fa[N][21], ans[N], nn, n, m, Q, K, S;struct Edge {int u, v, w;bool operator < (const Edge &t) const {return w > t.w;}
}edge[N];struct Node {int u, w;bool operator < (const Node &t) const {return w > t.w;}
};void add1(int x, int y, int w) {to1[cnt1] = y;nex1[cnt1] = head1[x];value1[cnt1] = w;head1[x] = cnt1++;
}void add(int x, int y) {to[cnt] = y;nex[cnt] = head[x];head[x] = cnt++;
}priority_queue<Node> q;void Dijkstra() {while (q.size()) {q.pop();}q.push({1, 0});memset(vis, 0, sizeof vis), memset(dis, 0x3f, sizeof dis);dis[1] = 0;while (q.size()) {int rt = q.top().u;q.pop();if (vis[rt]) {continue;}vis[rt] = 1;for (int i = head1[rt]; i; i = nex1[i]) {if (dis[to1[i]] > dis[rt] + value1[i]) {dis[to1[i]] = dis[rt] + value1[i];q.push({to1[i], dis[to1[i]]});}}}
}int find(int rt) {return ff[rt] == rt ? rt : ff[rt] = find(ff[rt]);
}void dfs(int rt, int f) {fa[rt][0] = f, ans[rt] = rt <= n ? dis[rt] : 0x3f3f3f3f;for (int i = 1; i <= 20; i++) {fa[rt][i] = fa[fa[rt][i - 1]][i - 1];}for (int i = head[rt]; i; i = nex[i]) {if (to[i] == f) {continue;}dfs(to[i], rt);ans[rt] = min(ans[rt], ans[to[i]]);}
}void kruskal() {for (int i = 1; i < N; i++) {ff[i] = i, head[i] = 0;}cnt = 1;sort(edge + 1, edge + 1 + m);for (int i = 1, cur = 1; i <= m && cur < n; i++) {int u = find(edge[i].u), v = find(edge[i].v);if (u != v) {cur++, nn++;ff[u] = nn, ff[v] = nn;value[nn] = edge[i].w;add(nn, u), add(nn, v);if (u <= n) {value[u] = edge[i].w;}if (v <= n) {value[v] = edge[i].w;}}}dfs(nn, 0);
}int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);int T;scanf("%d", &T);while (T--) {scanf("%d %d", &n, &m);nn = n;memset(head1, 0, sizeof head1), cnt1 = 1;for (int i = 1, u, v, l, a; i <= m; i++) {scanf("%d %d %d %d", &u, &v, &l, &a);add1(u, v, l), add1(v, u, l);edge[i] = {u, v, a};}Dijkstra();kruskal();scanf("%d %d %d", &Q, &K, &S);for (int i = 1, v, p, last_ans = 0; i <= Q; i++) {scanf("%d %d", &v, &p);v = (v + 1ll * K * last_ans - 1) % n + 1, p = (p + 1ll * K * last_ans) % (S + 1);for (int j = 20; j >= 0; j--) {if (fa[v][j] && value[fa[v][j]] > p) {v = fa[v][j];}}last_ans = ans[v];printf("%d\n", last_ans);}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313853.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

打不死我的,终将使我强大!DevOps黑客马拉松参赛心得

&#xff08;IDCF DevOps黑客马拉松到底是个啥活动&#xff1f;&#xff09;长得丑活得久、长得帅也惹人爱&#xff01;大家好&#xff0c;我是刘威。隆正信息的业务架构师-花名逸云。非常荣幸可以参加在北京举办的第一届DevOps黑客马拉松比赛。黑客马拉松不是突然冒出来的&…

Java线程池面试题

1、什么是线程池 java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池 多线程技术主要解决处理器单元内多个线程执行的问题&#xff0c;它可以显著减少处理器单元的闲置时间&#xff0c;增加处理器单元的吞吐能力。 假设一个服…

F. Cheap Robot(kruskal 重构树)

F. Cheap Robot 给定一个无向连通图&#xff0c;每条边有边权&#xff0c;路过需要消耗对应的电量&#xff08;边权&#xff09;&#xff0c;有kkk个中心点&#xff0c; 问从a−>ba-> ba−>b&#xff0c;我们最少需要带多少电&#xff0c;设最小为CCC&#xff0c;当…

「标签管理」使用标签管理有道云笔记资料

因着大家对文件标签化比较高难道&#xff0c;需要熟悉一个标签工具软件&#xff0c;所以今天暂时来介绍个简单一些的网络资料的标签化管理&#xff0c;使用有道云笔记作为落地工具&#xff0c;同理在OneNote、印象笔记上原理类似。有道云笔记免费功能够用为了选择哪个笔记产品&…

JDK线程池实现

参见&#xff1a; https://www.cnblogs.com/trust-freedom/p/6594270.html http://ifeve.com/java-threadpool/ https://blog.csdn.net/u010963948/article/details/80573898 https://blog.csdn.net/moakun/article/details/80606029 https://www.cnblogs.com/liuzhihu/p/81773…

Problem G. Pandaria(线段树合并 + Kruskal 重构树)

Problem G. Pandaria 给定一个有nnn条边的无向连通图&#xff0c;每条边有对应的边权&#xff0c;每个点有一个颜色&#xff0c; 问从一个点出发&#xff0c;经过不超过www的边权&#xff0c;所能到达的点中&#xff0c;颜色出现次数做多且颜色编号最小的是什么颜色。 不超过…

我如何吸引Elastic创始人一起对高并发写入进行优化?

导语&#xff1a;在腾讯金融科技数据应用部的全民 BI 项目里&#xff0c;我们每天面对超过 10 亿级的数据写入&#xff0c;提高 ES 写入性能迫在眉睫&#xff0c;在最近的一次优化中&#xff0c;有幸参与到了 Elasticsearch 开源社区中。背景为了更便捷地分析数据&#xff0c;腾…

Java线程安全

Java 线程安全 什么是线程安全&#xff1f; 当多个线程访问某个类时&#xff0c;这个类始终都能表现出正确的行为&#xff0c;那么就称这个类是线程安全的。 一&#xff1a;基本概念 共享资源&#xff1a;能够被多个线程同时访问的资源竞态条件&#xff1a;当两个线程竞争统…

P4899 [IOI2018] werewolf 狼人(kruskal 重构树 + 主席树)

P4899 [IOI2018] werewolf 狼人 给定一个有nnn个点mmm条边的无向图&#xff0c;有QQQ个询问 每次输入S,E,L,RS, E, L, RS,E,L,R&#xff0c;表示你在SSS点出发&#xff0c;要到EEE点&#xff0c;且初始时你是人形态&#xff0c;你只能走[L,n][L, n][L,n]的点&#xff0c; 但…

微软+开源,那些亲爱的以及热爱的

微软 Reactor 社区空间开幕式暨 Azure Meetup 社区活动已于9月7日在上海圆满结束&#xff01;但是…如何构建一个可持续发展的社区未来的路&#xff0c;仍然很长...你应该知道的微软 Reactor微软 Reactor 是微软为构建开发者社区而提供的一个社区空间&#xff0c;以“予力多元…

Java线程问题问答

1、多线程的作用&#xff1f; 发挥多核CPU的优势&#xff0c;提高效率防止阻塞便于建模&#xff1a;将一个任务拆分成多个子任务&#xff0c;分别建立程序模型 2&#xff1a;平时项目中使用锁和synchronized比较多&#xff0c;而很少使用volatile&#xff0c;难道就没有保证可见…

ASP.NET Core 2.2 项目升级至 3.0 备忘录

.NET Core 3.0及ASP.NET Core 3.0 前瞻ASP.NET Core 3.0 迁移避坑指南将 ASP.NET Core 2.2 迁移至 ASP.NET Core 3.0 需要注意的地方记录在这篇随笔中。TargetFramework 改为 netcoreapp3.0 <TargetFramework>netcoreapp3.0</TargetFramework>从 Web 项目&#xff…

Java 死锁

目录&#xff1a; 什么是死锁&#xff1f;死锁是怎么产生的&#xff1f;怎么排查死锁&#xff1f;死锁的预防拓展&#xff1a;Java CPU 100%排查 一 什么是死锁&#xff1f; 注&#xff1a;线程和进程都可能会产生死锁&#xff0c;以下以线程为例 死锁是指两个或两个以上的…

CF196E Opening Portals

CF196E Opening Portals 给定一个有nnn个节点&#xff0c;mmm条边的无向联通图&#xff0c;有kkk个点有portalsportalsportals&#xff0c;当经过了某个点&#xff0c;如果这个点有portalportalportal&#xff0c;它就会永久开启&#xff0c; 对于任意两个开启的portalportal…

.NET Core 微信小程序退款——(统一退款)

点击上方“dotNET名人堂”&#xff0c;选择“设为星标”用学习的姿态&#xff0c;步入工作的状态继上一篇".NET Core 微信小程序支付——&#xff08;统一下单&#xff09;后"&#xff0c;本文将实现统一退款功能&#xff0c;能支付就应该能退款嘛&#xff0c;一般涉…

Java 锁之 CAS

什么是CAS(compare and swap)&#xff1f; CAS&#xff08;Compare & Set&#xff0c;或是 Compare & Swap&#xff09;&#xff0c;即比较并交换&#xff0c;也是实现我们平时所说的自旋锁或乐观锁的核心操作。 它的实现很简单&#xff0c;就是用一个预期的值和内存…

牛客练习赛71 F 红蓝图(kruskal重构树)

红蓝图 给定两个参数x,tx, tx,t&#xff0c;删除边权大于ttt的红边&#xff0c;和边权小于ttt的蓝边&#xff0c;问对于所有的点yyy&#xff0c;既能通过红边走向xxx&#xff0c;又能通过蓝边走向xxx&#xff0c;的点有多少个。 考虑对红边按照边权升序建立一颗kruskalkruska…

.NET 分布式自增Id组件(解决自动分配机器Id、时间回拨问题)

IdHelper是一个.NET&#xff08;支持.NET45或.NET Standard2&#xff09;生成分布式趋势自增Id组件&#xff0c;有两个版本&#xff1a;原始版为基于雪花Id&#xff08;不了解请自行百度&#xff09;方案&#xff0c;需要手动管理设置WorkerId&#xff1b;完美版在原始版的基础…

悲观锁与乐观锁

悲观锁 总是假设最坏的情况&#xff0c;每次取数据的时候都认为别人会来修改&#xff0c;所以每次取数据的时候都会上锁。其它线程想要取这份数据就必须拿到相应的锁&#xff08;共享资源每次只供一个线程使用&#xff0c;其它线程阻塞&#xff0c;用完之后转让给其他线程&…

1190 最小公倍数之和 V2

1190 最小公倍数之和 V2 ∑iablcm(i,b)∑iabibgcd⁡(i,b)b∑d∣b∑i⌈ad⌉bdi[gcd(i,bd)1]b∑d∣b∑k∣bdμ(k)k∑i⌈⌈ad⌉k⌉abkib∑T∣n∑i⌈aT⌉bTi∑k∣Tμ(k)kb∑T∣n(bT⌈aT⌉)(bT−⌈aT⌉1)2∑k∣Tμ(k)k设f(n)∑d∣nμ(d)d,f(1)1,f(p)1−p,f(pk)1−p,且为积性函数\sum…