Peaks 线段树合并

\(n\)个带权值\(h_i\)山峰,有\(m\)条山峰间双向道路,\(q\)组询问,问从\(v_i\)开始只经过\(h_i\le x\)的路径所能到达的山峰中第\(k\)高的山峰,如果无解输出\(-1\)

线段树合并好题。吊打主席树、Kruskal重构树的典范

首先发现可以离线,我们将所有询问按\(x\)排序,随着询问再去加边,这样可以去掉路径上\(h_i\le x\)这一条件使问题极大简化。

然后从\(v_i\)开始能经过的所有山峰可以看做联通块,于是我们愉快地用并查集维护,用权值线段树查询第\(k\)大,合并联通块时合并权值线段树即可。很像[HNOI2012]永无乡

另外注意此题是查询第\(k\)大,不是第\(k\)小。

  1. #include <cstdio>
  2. #include <algorithm>
  3. #define MAXN 100010
  4. #define MAXM 500005
  5. using namespace std;
  6. int n,m,q;
  7. inline int read(){
  8. char ch=getchar();int s=0;
  9. while(ch<'0'||ch>'9') ch=getchar();
  10. while(ch>='0'&&ch<='9') s=s*10+ch-'0', ch=getchar();
  11. return s;
  12. }
  13. int h[MAXN],h_sort[MAXN],rnk[MAXN];
  14. struct nod{
  15. int u,v,val;
  16. } edge[MAXM];
  17. bool cmp_nod(const nod &a, const nod &b){
  18. return a.val<b.val;
  19. }
  20. struct question{
  21. int v,x,k;
  22. int id;
  23. } qs[MAXM];
  24. bool cmp_qs(const question &a, const question &b){
  25. return a.x<b.x;
  26. }
  27. int res[MAXM];
  28. int fa[MAXN];
  29. int get_fa(int x){
  30. if(fa[x]==x) return x;
  31. return fa[x]=get_fa(fa[x]);
  32. }
  33. #define MAXT MAXN*20
  34. int rot[MAXN],tot,s;
  35. int tre[MAXT],sl[MAXT],sr[MAXT];
  36. void change(int &x, int l, int r, int pos){
  37. if(x==0) x=++tot;
  38. tre[x]+=1;
  39. if(l==r) return;
  40. int mid=(l+r)>>1;
  41. if(pos<=mid) change(sl[x], l, mid, pos);
  42. else change(sr[x], mid+1, r, pos);
  43. }
  44. int query(int x, int l, int r, int k){
  45. if(x==0||tre[x]<k) return 0; // 如果不存在则返回0
  46. if(l==r) return l;
  47. int mid=(l+r)>>1;
  48. if(tre[sr[x]]>=k) return query(sr[x], mid+1, r, k); // 找第k大所以先找右儿子
  49. else return query(sl[x], l, mid, k-tre[sr[x]]);
  50. }
  51. int merge(int a, int b, int l, int r){
  52. if(a==0||b==0) return a+b;
  53. if(l==r){
  54. tre[a]+=tre[b];
  55. return a;
  56. }
  57. int mid=(l+r)>>1;
  58. sl[a]=merge(sl[a], sl[b], l, mid);
  59. sr[a]=merge(sr[a], sr[b], mid+1, r);
  60. tre[a]=tre[sl[a]]+tre[sr[a]]; // 记得合并后更新
  61. return a;
  62. }
  63. void merge_edge(int x){
  64. int fa1=get_fa(edge[x].u),fa2=get_fa(edge[x].v);
  65. if(fa1==fa2) return; // 如果已经都联通了则不需要合并线段树了
  66. fa[fa2]=fa1;
  67. rot[fa1]=merge(rot[fa1], rot[fa2], 1, s);
  68. }
  69. int main(){
  70. n=read(),m=read(),q=read();
  71. for(int i=1;i<=n;++i) fa[i]=i;
  72. for(int i=1;i<=n;++i) h_sort[i]=h[i]=read();
  73. sort(h_sort+1, h_sort+1+n);
  74. s=unique(h_sort+1, h_sort+1+n)-(h_sort+1);
  75. for(int i=1;i<=n;++i){
  76. rnk[i]=lower_bound(h_sort+1, h_sort+1+s, h[i])-h_sort; // 离散化
  77. change(rot[i], 1, s, rnk[i]);
  78. }
  79. for(int i=1;i<=m;++i) edge[i].u=read(),edge[i].v=read(),edge[i].val=read();
  80. sort(edge+1, edge+1+m, cmp_nod);
  81. for(int i=1;i<=q;++i) qs[i].v=read(),qs[i].x=read(),qs[i].k=read(),qs[i].id=i;
  82. sort(qs+1, qs+1+q, cmp_qs);
  83. int pos=1;
  84. for(int i=1;i<=q;++i){
  85. int x=qs[i].x,v=qs[i].v,k=qs[i].k;
  86. while(pos<=m&&edge[pos].val<=x) merge_edge(pos),++pos; // 把小于等于x的边加入
  87. int ans=query(rot[get_fa(v)], 1, s, k);
  88. if(ans==0) res[qs[i].id]=-1;
  89. else res[qs[i].id]=h_sort[ans]; // 最后答案不是下标而是对应的值
  90. }
  91. for(int i=1;i<=q;++i) printf("%d\n", res[i]);
  92. return 0;
  93. }

Peaks 线段树合并的更多相关文章

  1. 【bzoj3545】[ONTAK2010]Peaks 线段树合并

    [bzoj3545][ONTAK2010]Peaks 2014年8月26日3,1512 Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路 ...

  2. BZOJ.3545.[ONTAK2010]Peaks(线段树合并)

    题目链接 \(Description\) 有n个座山,其高度为hi.有m条带权双向边连接某些山.多次询问,每次询问从v出发 只经过边权<=x的边 所能到达的山中,第K高的是多少. \(Solut ...

  3. bzoj3545 Peaks 线段树合并

    离线乱搞... 也就是一个线段树合并没什么 #include<algorithm> #include<iostream> #include<cstring> #in ...

  4. 【线段树合并】bzoj3545: [ONTAK2010]Peaks

    1A还行 Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问, ...

  5. [BZOJ3545] [ONTAK2010]Peaks(线段树合并 + 离散化)

    传送门 由于困难值小于等于x这个很恶心,可以离线处理,将边权,和询问时的x排序. 每到一个询问的时候,将边权小于等于x的都合并起来再询问. .. 有重复元素的线段树合并的时间复杂度是nlog^2n # ...

  6. BZOJ3545 Peaks 离线处理+线段树合并

    题意: 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经 ...

  7. 线段树合并&&启发式合并笔记

    这俩东西听起来很高端,实际上很好写,应用也很多~ 线段树合并 线段树合并,顾名思义,就是建立一棵新的线段树保存原有的两颗线段树的信息. 考虑如何合并,对于一个结点,如果两颗线段树都有此位置的结点,则直 ...

  8. [XJOI NOI2015模拟题13] C 白黑树 【线段树合并】

    题目链接:XJOI - NOI2015-13 - C 题目分析 使用神奇的线段树合并在 O(nlogn) 的时间复杂度内解决这道题目. 对树上的每个点都建立一棵线段树,key是时间(即第几次操作),动 ...

  9. [BZOJ 2212] [Poi2011] Tree Rotations 【线段树合并】

    题目链接:BZOJ - 2212 题目分析 子树 x 内的逆序对个数为 :x 左子树内的逆序对个数 + x 右子树内的逆序对个数 + 跨越 x 左子树与右子树的逆序对. 左右子树内部的逆序对与是否交换 ...

随机推荐

  1. k8s 启动pod的问题

    版本: k8s 1.5 docker 1.3 CentOS 7.6 使用命令 kubectl get pods输出no resources.解决方法是修改 apiserver 的配置文件 vim /e ...

  2. Linux追加磁盘扩展

    一:查看磁盘空间信息: fdisk -l 查看当前的系统的磁盘空间的情况: 二:增加分区: fdisk /dev/sda 键入n,增加一个分区,得到: 键入 p,主分区,并键入3(编号): 默认起始扇 ...

  3. hdu 2555

    Problem Description 杭州师范大学第29届田径运动会圆满的闭幕了,本届运动会是我校规模最大,参赛人数最多的一次运动会.在两天半时间里,由学生.教工组成的61支代表队共2664名运动员 ...

  4. Python基础初识

    一.安装 暂时没空写,预留 二.python基础初识 2.1 注释 当行注释:# 被注释内容 多行注释:'''被注释内容''',或者"""被注释内容"" ...

  5. Kafka架构和原理深度剖析

    Kafka简介 Kafka是一种分布式的,基于发布/订阅的消息系统.主要设计目标如下: 以时间复杂度为O(1)的方式提供消息持久化能力,并保证即使对TB级以上数据也能保证常数时间的访问性能 高吞吐率. ...

  6. MySQL常见问题集锦及注意事项

    一.表设计上的坑 1.字段设计 1.1 字段类型设计 尽量使用整型表示字符串: `INET_ATON(str)`,address to number `INET_NTOA(number)`,numbe ...

  7. kubernetes 应用快速入门

    使用kubectl进行增.删.查.改等常用操作 查看kubectl命令帮助 kubectl -h kubectl controls the Kubernetes cluster manager. Fi ...

  8. iveiw DatePicker 只能选择本月之前的日期,本月包括之后都不能选择

    日期判断只能选择本月之前的日期 <DatePicker type="date" :options="options3" format="yyyy ...

  9. cmd中for的用法

    在cmd窗口输入for /?后的原文,被我自己“翻译”了一下,更像人话了. 推荐去https://www.cnblogs.com/cbugs/p/8992059.html这篇部落格里去看看,讲的更好. ...

  10. Linux服务器集群代理配置

    因为之前本科参与开发的一个互联网新闻采集系统需要爬取几个国外的新闻站点,通过翻墙才能访问,而我们的服务器是阿里云服务器,没有操作界面,而且抽取任务是定时执行,必须要实现程序控制VPN的连接与断开.所以 ...