题目链接:

https://www.luogu.org/problemnew/show/P4197

题目:

在Bytemountains有N座山峰,每座山峰有他的高度$h_i$。有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走

现在有Q组询问,每组询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1。

在线做法题解:

一句话题解:kruskal重构树dfs序上建主席树直接查询第k大即可

知识点拓展:

下面讲讲kruskal重构树是干啥的?这是我第一次做kruskal重构树的题目,下面就当是学习笔记了

从这道题来看,显然我们只需要考虑最小生成树上的路径即可

那么所谓kruskal重构树,就是在kruskal的算法过程中搞一波事情。思想就是在建最小生成树的时候不是直接连边,而是新建一个节点,并把这个节点的值设为边权,然后令两个连通块的代表点分别作为它的左右儿子。然后令这个新节点成为整个连通块的代表点

显然这棵树会具备这样的一个性质:从一个节点向他的儿子一路dfs下去,遍历到的点的点权都是单调递减的,因为我们是排序之后不断加边的(父亲肯定比儿子后建立)

那么我们发现原来的每个节点其实就是这棵子树中的叶子节点,要查询节点x出发经过边权不大于val的路径的点,我们就从节点x不断向上直到找到一个最远的祖先的点权刚好小于等于val,这个祖先的子树中的点显然都满足到x的路径上的边都不超过val

怎么找呢?我们可以倍增

这道题中由于我们要查询第k大,也就是我们要对每个节点的子树中的叶子节点维护第k大。显然可以直接转化为dfs序上查询区间第k大,因此我们还需要主席树

据某大佬说,kruskal重构树可以用来解决一系列“查询从某个点出发经过边权不超过val的边所能到达的节点”的问题,可以和其他数据结构(比如主席树)套用来维护更加复杂的询问

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;const int N=1e5+15;
const int M=5e5+15;
const int NN=N<<2;
const int inf=1e9+7;
int n,m,q,xys,tim,tot,cnt;
int height[N];
int fa[NN][50],value[NN],in[NN],st[NN],ed[NN],head[NN],leaf[NN],pos[NN],root[NN],f[NN];
int lx[NN<<5],rx[NN<<5],siz[NN<<5];
struct E
{int x,y,w;
}e[M];
struct EDGE
{int to,nxt;
}edge[NN];
inline int read()
{char ch=getchar();int s=0,f=1;while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}return s*f;
}
bool cmp(E a,E b) {return a.w<b.w;}
void add(int u,int v)
{edge[++tot]=(EDGE){v,head[u]};head[u]=tot;
}
int find(int x)
{if (f[x]!=x) f[x]=find(f[x]);return f[x];
}
void dfs(int x)
{pos[++tim]=x;st[x]=tim;for (int i=1;(1<<i)<=(n<<1);i++) fa[x][i]=fa[fa[x][i-1]][i-1];for (int i=head[x];i;i=edge[i].nxt){int y=edge[i].to;if (y==fa[x][0]) continue;fa[y][0]=x;dfs(y);leaf[x]+=leaf[y];}if (!leaf[x]) leaf[x]=1;ed[x]=tim;
}
int update(int pre,int l,int r,int x)
{int o=++cnt;lx[o]=lx[pre];rx[o]=rx[pre];siz[o]=siz[pre]+1;if (l==r) return o;int mid=l+r>>1;if (x<=mid) lx[o]=update(lx[pre],l,mid,x);else rx[o]=update(rx[pre],mid+1,r,x);return o;
}
int kth(int lr,int rr,int l,int r,int k)
{if (l==r) return l;int x=siz[rx[rr]]-siz[rx[lr]];int mid=l+r>>1;if (k<=x) return kth(rx[lr],rx[rr],mid+1,r,k);else return kth(lx[lr],lx[rr],l,mid,k-x);
}
int query(int v,int x,int k)
{for (int i=24;i>=0;i--) if (value[fa[v][i]]<=x&&fa[v][i]) v=fa[v][i];if (leaf[v]<k) return -1;int l=st[v],r=ed[v];return kth(root[l-1],root[r],0,inf,k);
}
int main()
{n=read();m=read();q=read();for (int i=1;i<=n;i++) height[i]=read();for (int i=1;i<=m;i++){e[i].x=read();e[i].y=read();e[i].w=read();}sort(e+1,e+1+m,cmp);xys=n;//原来节点的标号从1-n for (int i=1;i<=n<<1;i++) f[i]=i;for (int i=1;i<=m;i++){int fx=find(e[i].x),fy=find(e[i].y);if (fx!=fy){value[++xys]=e[i].w;f[fx]=f[fy]=xys;add(xys,fx);add(xys,fy);in[fx]++;in[fy]++;}}for (int i=1;i<=xys;i++) if (!in[i]) dfs(i);for (int i=1;i<=xys;i++){if (pos[i]<=n) root[i]=update(root[i-1],0,inf,height[pos[i]]);else root[i]=root[i-1];}while (q--){int v=read(),x=read(),k=read();printf("%d\n",query(v,x,k);}return 0;
}

离线做法题解:

同样我们要在kruskal的算法流程上做文章

考虑把询问按x排序,每次把不大于这个长度的边加入最小生成树,然后对每个联通块建主席树,加边合并主席树。最后再当前查询点所在的主席树中查询第k大即可

这个好想一点

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
#define mid ((l+r)>>1)
using namespace std;const int M=5e5+15;
const int N=1e5+15;
const int inf=1e9;
int n,m,Q,tot;
int rt[N],fa[N],lx[N<<5],rx[N<<5],siz[N<<5],ans[M],hei[N];
struct EDGE
{int x,y,w;
}e[M];
struct Que
{int id;int v,x,k;
}q[M];
bool operator < (EDGE x,EDGE y) {return x.w<y.w;}
bool operator < (Que x,Que y) {return x.x<y.x;}
inline int read()
{char ch=getchar();int s=0,f=1;while (ch<'0'|ch>'9') {if (ch=='-') f=-1;ch=getchar();}while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}return s*f;
}
int find(int x)
{if (fa[x]!=x) fa[x]=find(fa[x]);return fa[x];
}
void update(int &o,int l,int r,int x)
{if (!o) o=++tot;siz[o]++;if (l==r) return;if (x<=mid) update(lx[o],l,mid,x);else update(rx[o],mid+1,r,x);
}
void merge(int &x,int y)
{if (!x||!y) {x=x|y;return;}siz[x]+=siz[y];merge(lx[x],lx[y]);merge(rx[x],rx[y]);
}
int kth(int o,int l,int r,int k)
{if (l==r) return l;if (siz[rx[o]]>=k) return kth(rx[o],mid+1,r,k);else return kth(lx[o],l,mid,k-siz[rx[o]]);
}
int main()
{n=read();m=read();Q=read();for (int i=1;i<=n;i++) fa[i]=i,hei[i]=1,update(rt[i],0,inf,read());for (int i=1;i<=m;i++){e[i].x=read();e[i].y=read();e[i].w=read();}sort(e+1,e+1+m);for (int i=1;i<=Q;i++){q[i].id=i;q[i].v=read();q[i].x=read();q[i].k=read();}sort(q+1,q+1+Q);int l=1,cnt=0;for (int i=1;i<=Q;i++){int v=q[i].v,x=q[i].x,k=q[i].k;while (e[l].w<=x&&l<=m){if (cnt==n-1) break;int f1=find(e[l].x),f2=find(e[l].y);++l;if (f1==f2) continue;++cnt;if (hei[f1]>hei[f2])//启发式合并
            {merge(rt[f1],rt[f2]);fa[f2]=f1;}else if (hei[f1]<hei[f2]){merge(rt[f2],rt[f1]);fa[f1]=f2;}else {merge(rt[f1],rt[f2]);fa[f2]=f1;hei[f1]++;}}v=find(v);if (siz[rt[v]]<k) ans[q[i].id]=-1;else ans[q[i].id]=kth(rt[v],0,inf,k);}for (int i=1;i<=Q;i++) printf("%d\n",ans[i]);return 0;
} 

转载于:https://www.cnblogs.com/xxzh/p/9768183.html

[luogu P4197] Peaks 解题报告(在线:kruskal重构树+主席树 离线:主席树+线段树合并)...相关推荐

  1. CodeForces - 1417F Graph and Queries(克鲁斯卡尔重构树的dfs序上建线段树)

    题目链接:点击查看 题目大意:给出一个 n 个点 m 条边组成的无向图,每个点初始时都有一个权值 val,满足: 每个点的 val[ i ] 各不相同 val[ i ] ∈ [ 1 , n ] 现在有 ...

  2. kruskal重构树练习

    洛谷 P4197 Peaks 题意: 有 nnn 个山峰,每一个山峰高 hih_ihi​ ,有 mmm 条双向带权边将一些山峰连接起来,有 qqq 次询问,每次询问 (v,x,k)(v,x,k)(v, ...

  3. [ONTAK2010] Peaks加强版 (kruskal重构树+主席树+倍增)

    Peaks description solution code description 在Bytemountains有N座山峰,每座山峰有他的高度h_i 有些山峰之间有双向道路相连,共M条路径,每条路 ...

  4. AGC002(D~F)【Kruskal重构树,博弈论,dp】

    正题 AT1998 [AGC002D] Stamp Rally[Kruskal重构树,倍增] https://www.luogu.com.cn/problem/AT1998 题目大意 给出nnn个点m ...

  5. 【NOI 2018】归程(Kruskal重构树)

    题面在这里就不放了. 同步赛在做这个题的时候,心里有点纠结,很容易想到离线的做法,将边和询问一起按水位线排序,模拟水位下降,维护当前的各个联通块中距离$1$最近的距离,每次遇到询问时输出所在联通块的信 ...

  6. kruskal 重构树(讲解 + 例题)

    kruskal重构树 如何建树 模仿kruskalkruskalkruskal,先将所有边排序. 依次遍历每一条边,如果这条边的两个节点(u,vu, vu,v)不在同一个连通块里面, 则新建一个nod ...

  7. [NOI2018] 归程(线段树维护并查集的可持久化/kruskal重构树,倍增+dijkstra最短路)

    [NOI2018] 归程 description solution1 code1 solution2 code description 题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要 ...

  8. 【kruskal重构树】【主席树】werewolf 狼人(P4899)

    正题 P4899 题目大意 给你一个图,对于每次询问Si,Ei,Li,RiS_i,E_i,L_i,R_iSi​,Ei​,Li​,Ri​,回答从SiS_iSi​走到EiE_iEi​,是否存在路径满足前面 ...

  9. LOJ.2865.[IOI2018]狼人(Kruskal重构树 主席树)

    LOJ 洛谷 这题不就是Peaks(加强版)或者归程么..这算是\(IOI2018\)撞上\(NOI2018\)的题了? \(Kruskal\)重构树(具体是所有点按从小到大/从大到小的顺序,依次加入 ...

  10. Kruskal重构树 学习笔记

    Kruskal重构树 学习笔记 文章目录 Kruskal重构树 学习笔记 前言 例题1 BZOJ3732 Network 例题2 [NOI2018] 归程 前言 Kruskal重构树是一种比较冷门的算 ...

最新文章

  1. Python实现线程池
  2. Win32下内存分配简单示例 - 使用CFree
  3. 数学建模 TSP(旅行商问题) Lingo求解
  4. 唤醒控件曾经拥有的能力
  5. 红黑树结构完整实现与详解
  6. 如何做一名出色的初级开发?
  7. bzoj 4498: 魔法的碰撞(DP+组合数)
  8. 如果你被这个视频深深地震撼!那你一定是幸运的!
  9. cuteftp 9 显示中文乱码
  10. 微信内置浏览器打开所有页面空白解决方案
  11. jQuery创建表格
  12. Proxmox VE 配置桌面虚拟化
  13. 共享单车信息系统服务器部署,共享单车云服务器
  14. Photoshop-为图像添加一个真实投影
  15. Markov链:初始概率、绝对概率
  16. 真是没有预料到,一款推送全国公考信息的app开发用了一年时间
  17. 02#EXCEL函数【基础】
  18. 数据库的三个范式(sql server ,oracle ,db2...)
  19. 7.4 初等矩阵和可逆性
  20. Linux安装mysql(mysql-5.7.23-1.el7.x86_64.rpm-bundle.tar)(万能解决登录问题,最详细教程)

热门文章

  1. cisco接口模式转换
  2. Android系统的开机画面显示过程分析(12)
  3. 居中 html css
  4. Innosetup打包自动下载.net framework 动态库及替换卸载程序图标.
  5. 【java集合框架源码剖析系列】java源码剖析之HashMap
  6. Makefile中变量赋值方式
  7. 私活后的 WPF 设计经验总结
  8. 关于ADO之AddNew,UPdate与Identity列
  9. 企业网络管理员如何有效封杀QQ
  10. es 测试数据进行 增删查改