P4197 Peaks [克鲁斯卡尔重构树 + 主席树]

部分kruskal重构树内容摘抄于bzt神仙的blog

Problem

在\(Bytemountains\)有\(n\)座山峰,每座山峰有他的高度\(h_i\) 。有些山峰之间有双向道路相连,共\(M\)条路径,每条路径有一个困难值,这个值越大表示越难走,现在有\(Q\)组询问,每组询问询问从点\(v\)开始只经过困难值小于等于\(x\)的路径所能到达的山峰中第\(k\)高的山峰,如果无解输出\(-1\)。

kruskal重构树是用来解决一些诸如“查询从某个点开始 经过边权不超过\(val\)所能到达的节点”的问题
所以很显然 在最小生成树上是最优的。。其他多余的边可以不需要考虑。。
那我们在跑kruskal的时候重新建边。。
克鲁斯卡尔重构树的思想就是在建最小生成树的时候不是直接连边 而是新建一个节点 并把这个节点的值设为边权 然后令两个连通块的代表点分别作为它的左右儿子 然后令这个新节点成为整个连通块的代表点
即如果\(u\) \(v\)不连通
把\(u\) \(v\)两点连到新建节点上。。顺便连边。。然后就变成了一棵树。。

sort(q + 1 , q + m + 1 , cmp) ;
  for( int i = 1 ; i <= n ; i ++ ) fa[i] = i ;
  tot = n ;
  for( int i = 1 ; i <= m ; i ++ ) {
    int u = find(q[i].u) , v = find(q[i].v) ;
    if(u == v) continue ;
    val[++ tot] = q[i].w ; fa[tot] = fa[u] = fa[v] = tot ;
    add(tot , u) ; add(tot , v) ;
  }

以下内容来自于 bztminamoto 的 blog

假设现在有四个节点,要求他们的克鲁斯卡尔重构树

我们按最小生成树的方法找,先把边按权值从小到大排序。

然后设第一条边权值为4,连接1和2这两个连通块

然后新建一个节点5,点权设为4,并把1和2作为他的左右儿子

第二条边权值为6,连接3和4这两个连通块

然后新建一个节点6,点权设为6,并把3和4作为他的左右儿子

第三条边权值为7,连接1和2,那么我们就是要把4和6的连通块相连了(这两个是连通块的代表点)

然后新建一个节点7,点权设为7,并把5和6作为他的左右儿子

然后这一棵克鲁斯卡尔重构树就建好了

然后这道题求的是 \(k\) 大
\(k\) 大 问题一般都是主席树解决。。
所以把新建的树变成dfs序放到主席树上解决就可以了(在线)。。

#include<bits/stdc++.h>
using namespace std ;
const int N = 2e5 + 5 ;
const int M = 5e5 + 5 ;
const int inf = INT_MAX ;
struct node { int v , nxt ; } e[N << 1] ;
struct Node { int u , v , w ; } q[M] , t[N << 1] ;
int n , m , Q ;
int rt[N] , h[N] ;
int ls[N << 5] , rs[N << 5] , sum[N << 5] ;
int val[N] ;
int head[N] , cnt = 0 ;
inline void add(int u , int v) { e[++ cnt] = { v , head[u]} ; head[u] = cnt ; }
inline bool cmp(Node x , Node y) { return x.w < y.w ; }
int fa[N] ; int tot ;
inline int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]) ; }
inline void kruskal() {
  sort(q + 1 , q + m + 1 , cmp) ;
  for( int i = 1 ; i <= n ; i ++ ) fa[i] = i ;
  tot = n ;
  for( int i = 1 ; i <= m ; i ++ ) {
    int u = find(q[i].u) , v = find(q[i].v) ;
    if(u == v) continue ;
    val[++ tot] = q[i].w ; fa[tot] = fa[u] = fa[v] = tot ;
    add(tot , u) ; add(tot , v) ;
  }
}
int f[N][22], L[N] , R[N] ;
int b[N] , pop = 0 ;
inline void dfs(int u , int fa) {
  f[u][0] = fa ;
  for( int i = 1 ; i <= 19 ; i ++ ) f[u][i] = f[f[u][i - 1]][i - 1] ;
  L[u] = pop ;
  if(! head[u]) { b[++ pop] = u ; R[u] = pop ; return ; }
  for( int i = head[u] ; i ; i = e[i].nxt) {
    int v = e[i].v ; dfs(v , u) ;
  } R[u] = pop ;
}
inline void build(int l , int r , int & o) {
  o = ++ cnt ;
  if(l == r) return ;
  int mid = l + r >> 1 ;
  build(l , mid , ls[o]) ; build(mid + 1 , r , rs[o]) ;
}
inline void upd(int l , int r , int pre , int & o , int x) {
  o = ++ cnt ;
  ls[o] = ls[pre] ; rs[o] = rs[pre] ; sum[o] = sum[pre] ;
  if(l == r) { sum[o] ++ ; return ; }
  int mid = l + r >> 1 ;
  if(x <= mid) upd(l , mid , ls[pre] , ls[o] , x) ;
  else upd(mid + 1 , r , rs[pre] , rs[o] , x) ;
  sum[o] = sum[ls[o]] + sum[rs[o]] ;
}
inline int find_fa(int x , int k) {
  for(register int i = 19 ; ~ i ; i --)
    if(val[f[x][i]] <= k) x = f[x][i] ;
  return x ;
}
inline int query(int a , int b , int l , int r , int k) {
  if(l == r)
    if(k == sum[b] - sum[a]) return l ;
    else return 0 ;
  int x = sum[rs[b]] - sum[rs[a]] , mid = l + r >> 1 ;
  if(x >= k) return query(rs[a] , rs[b] , mid + 1 , r , k) ;
  else return query(ls[a] , ls[b] , l , mid , k - x) ;
}
signed main() {
  ios :: sync_with_stdio(false) ;
  cin.tie(nullptr) ;
  cout.tie(nullptr) ;
  cin >> n >> m >> Q ;
  val[0] = inf ;
  for( int i = 1 ; i <= n ; i ++ ) cin >> h[i] ;
  for( int i = 1 ; i <= n ; i ++ ) t[i].u = i , t[i].w = h[i] ;
  sort(t + 1 , t + n + 1 , cmp) ;
  for( int i = 1 ; i <= n ; i ++ ) h[t[i].u] = i ;
  for( int i = 1 ; i <= m ; i ++ ) { cin >> q[i].u >> q[i].v >> q[i].w ; }
  kruskal() ;
  dfs(tot , tot) ; cnt = 0 ;
  build(1 , n , rt[0]) ;
  for( int i = 1 ; i <= pop ; i ++)
    upd(1 , n , rt[i - 1] , rt[i] , h[b[i]]) ;
  t[0].w = -1 ;
  for( int i = 1 ; i <= Q ; i ++) {
    int v , x , k ; cin >> v >> x >> k ;
    int fa = find_fa(v , x) ;
    int ans = query(rt[L[fa]] , rt[R[fa]] , 1 , n , k) ;
    cout << t[ans].w << '\n' ;
  }
  return 0 ;
}

原文地址:https://www.cnblogs.com/Isaunoya/p/11780762.html

时间: 2024-09-29 10:35:29

P4197 Peaks [克鲁斯卡尔重构树 + 主席树]的相关文章

[您有新的未分配科技点][BZOJ3545&amp;BZOJ3551]克鲁斯卡尔重构树

这次我们来搞一个很新奇的知识点:克鲁斯卡尔重构树.它也是一种图,是克鲁斯卡尔算法求最小生成树的升级版首先看下面一个问题:BZOJ3545 Peaks. 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走. 现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1.N<=1e5,M,Q<=5*1e5 上面这个题没有要求在线,因此我们可以离线构造

克鲁斯卡尔重构树小结

https://zybuluo.com/ysner/note/1239639 定义 克鲁斯卡尔重构树可以维护诸如"查询从某个点出发经过边权不超过\(w\)的边最远所能到达的节点"或"从某点到某点所有路径的最长边的最小值"之类的问题. 总之,算法处理范围有限,且多为同时包含"最大最小".离线可二分的题目. 可与数据结构结合,以维护更复杂的数据结构. 它可以在线回答,复杂度为\(O(logn)\). 构建 把边权从大到小排序,用给两端点(两个联通块)

克鲁斯卡尔重构树

大佬博客定义克鲁斯卡尔重构树可以维护诸如"查询从某个点出发经过边权不超过的边最远所能到达的节点"或"从某点到某点所有路径的最长边的最小值"之类的问题.总之,算法处理范围有限,且多为同时包含"最大最小".离线可二分的题目. 可与数据结构结合,以维护更复杂的数据结构. 它可以在线回答,复杂度为O(logn) 构建把边权从大到小排序,用给两端点(两个联通块)新建一个权值为边权的共同父亲,来代表给它们加了一条边. 性质树上除叶子结点以外的点都对应着原来生

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include

BZOJ 3551 ONTAK2010 Peaks加强版 Kruskal重构树+可持久化线段树

题目大意:同3545 强制在线 3545题解传送门:http://blog.csdn.net/popoqqq/article/details/40660953 强制在线没法排序 启发式合并也就用不了了 Kruskal重构树是个挺好玩的东西 可以拿来处理一些最小生成树的边权最值问题 这里我们Kruskal连边时并不直接连边 而是新建一个节点ext 将两个点所在子树都连到ext的儿子上 比如说样例的树就建成了这样 图中红色的是原图的边权,黑色的是原图上的点 这样生成的树有一些十分优美的性质: 1.二

bzoj 3551 [ONTAK2010]Peaks加强版(kruskal,主席树,dfs序)

Description [题目描述]同3545 Input 第一行三个数N,M,Q. 第二行N个数,第i个数为h_i 接下来M行,每行3个数a b c,表示从a到b有一条困难值为c的双向路径. 接下来Q行,每行三个数v x k,表示一组询问.v=v xor lastans,x=x xor lastans,k=k xor lastans.如果lastans=-1则不变. Output 同3545 [思路] Kruskal+主席树+dfs序 一个叫kruskal重构树的方法QWQ.在kruskal合

【BZOJ 3545】【ONTAK 2010】Peaks &amp; 【BZOJ 3551】【ONTAK 2010】Peaks加强版 Kruskal重构树

sunshine的A题我竟然调了一周!!! 把循环dfs改成一个dfs就可以,,,我也不知道为什么这样就不会RE,但它却是A了,,, 这周我一直在调这个题,总结一下智障错误: 1.倍增的范围设成了n而不是n*2-1,,, 2.重构树的顶点是n*2-1,而我一开始设成了n,,, 3.define里的for3和for4的i--打成i++,,,,,,,,,,,, 4.dfs爆栈了,找CA爷问的编译命令里手动扩栈,真是愚蠢的问题,,,, 比赛时绝不会有太多时间,在这么犯逗就得滚粗了QAQ 3545: #

笛卡尔树及克鲁斯卡尔重构树

笛卡尔树 好东西, 可以用于最大(小)值分治, 在\(O(n)\)的时间复杂度内建出一个节点为区间最大值的树, 每次分治时走小区间可以保证\(O(nlog_n)\)的复杂度 建树时维护极右链, 他的中序遍历即原序列 代码 #include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,v[N],fa[N],ls[N],rs[N]; int s[N],top; void Tree() { for(int i = 1;

[IOI2018] werewolf 狼人 kruskal重构树,主席树

[IOI2018] werewolf 狼人 LG传送门 kruskal重构树好题. 日常安利博客文章 这题需要搞两棵重构树出来,这两棵重构树和我们平时见过的重构树有点不同(据说叫做点权重构树?),根据经过我们简化的建树方法,这两棵树不再是二叉树,但是仍具有kruskal重构树的优秀性质,建议结合后面的描述理解. 看这题需要首先我们从\(S\)走到\(T\)转化为分别从\(S\)和\(T\)出发寻找能共同到达的点,需要快速求出从某个点出发经过点权不大(小)于\(r\)(\(l\))的点,考虑kru