P3224 [HNOI2012]永无乡 题解

题意概括

有若干集合,每个集合最初包含一个值,和一个编号1~n。两个操作:合并两个集合,查询包含值x的集合中第k大值最初的集合编号。

思路

维护集合之间关系显然用并查集,但怎么处理询问,如果只是问最大值,那么显然可以用线段树把最大值存在并查集的祖先上,当然线段树也行。但这里问的是第k大。主席树?主席树是用来处理区间第k大的,而这里每棵树显然储存一整个集合(由多个小集合合并来的)的信息,我们并不关心这个集合内的区间问题,主席树便有点大材小用。所以,得出结论:用并查集和值域(权值)线段树合并。

你的线段树本来就能合并,那并查集是干嘛的呢?我们每次合并是取出x集合和y集合对应的A树和B树,并将B树的信息放到A上,就像这样:A B=>A+B B 如果是 A B=>A+B A+B或A B C(=A+B) 空间势必会炸。若不用并查集我们在查询集合y时,可能拿到的rt[y]树y的根就可能是 A B=>A+B B 中的那个B的而不是A+B的。用了并查集先把 f[y]=x 这样 rt[f[y] 就是 A+B 的根了。(不知不觉写了好多)

实现

其实实现很简单,但对于不熟悉值域线段树和线段树合并的人就不容易了。

值域线段树

先放这道题里涉及值域线段树的代码(代码总是比人话好理解):

  1. int newt(int l,int r,int val)//建一棵只有一个值的树
  2. {
  3. int now=++tot;
  4. t[now].v=1;
  5. if(l==r)
  6. return now;
  7. int mid=l+r>>1;
  8. if(mid>=val)
  9. t[now].l=newt(l,mid,val);
  10. else
  11. t[now].r=newt(mid+1,r,val);
  12. pushup(now);
  13. return now;
  14. }
  15. int query(int now,int l,int r,int val)//查询
  16. {
  17. if(t[now].v<val||!now)return 0;
  18. if(l==r)
  19. return l;
  20. int mid=(l+r)>>1;
  21. if(val<=t[t[now].l].v)
  22. return query(t[now].l,l,mid,val);
  23. else
  24. return query(t[now].r,mid+1,r,val-t[t[now].l].v);
  25. }

详细请看这位大佬的blog

线段树合并

  1. int merge(int x,int y,int l,int r)
  2. {
  3. if(!x||!y)return x+y;
  4. if(l==r){t[x].v=t[x].v+t[y].v;return x;}
  5. int mid=l+r>>1;
  6. t[x].l=merge(t[x].l,t[y].l,l,mid);
  7. t[x].r=merge(t[x].r,t[y].r,mid+1,r);
  8. pushup(x);
  9. return x;
  10. }

请看这位蒟蒻(我)的blog

AC代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. struct TREE
  4. {
  5. int v,l,r;
  6. }t[5000005];
  7. int n,m,p[100005],f[100005],rt[100005],id[100005],q,x,y,u,v,tot,k;
  8. char op;
  9. void pushup(int now)
  10. {
  11. t[now].v=t[t[now].l].v+t[t[now].r].v;
  12. }
  13. int find(int now)
  14. {
  15. if(f[now]==now)return now;
  16. else return f[now]=find(f[now]);
  17. }
  18. int newt(int l,int r,int val)
  19. {
  20. //cout<<l<<' '<<r<<' '<<val<<endl;
  21. int now=++tot;
  22. t[now].v=1;
  23. if(l==r)
  24. return now;
  25. int mid=l+r>>1;
  26. if(mid>=val)
  27. t[now].l=newt(l,mid,val);
  28. else
  29. t[now].r=newt(mid+1,r,val);
  30. pushup(now);
  31. return now;
  32. }
  33. int merge(int x,int y,int l,int r)
  34. {
  35. //cout<<x<<' '<<y<<' '<<l<<' '<<r<<endl;
  36. if(!x||!y)return x+y;
  37. if(l==r){t[x].v=t[x].v+t[y].v;return x;}
  38. int mid=l+r>>1;
  39. t[x].l=merge(t[x].l,t[y].l,l,mid);
  40. t[x].r=merge(t[x].r,t[y].r,mid+1,r);
  41. pushup(x);
  42. return x;
  43. }
  44. int query(int now,int l,int r,int val)
  45. {
  46. //cout<<now<<' '<<l<<' '<<r<<endl;
  47. if(t[now].v<val||!now)return 0;
  48. if(l==r)
  49. return l;
  50. int mid=(l+r)>>1;
  51. if(val<=t[t[now].l].v)
  52. return query(t[now].l,l,mid,val);
  53. else
  54. return query(t[now].r,mid+1,r,val-t[t[now].l].v);
  55. }
  56. void pp()
  57. {
  58. for(int i=1;i<=tot;i++)cout<<setw(2)<<i<<' ';cout<<endl;
  59. for(int i=1;i<=tot;i++)cout<<setw(2)<<f[i]<<' ';cout<<endl;
  60. for(int i=1;i<=tot;i++)cout<<setw(2)<<rt[i]<<' ';cout<<endl;
  61. cout<<endl;
  62. for(int i=1;i<=tot;i++)cout<<setw(2)<<t[i].v<<' ';cout<<endl;
  63. for(int i=1;i<=tot;i++)cout<<setw(2)<<t[i].l<<' ';cout<<endl;
  64. for(int i=1;i<=tot;i++)cout<<setw(2)<<t[i].r<<' ';cout<<endl;
  65. cout<<endl;
  66. }
  67. int main()
  68. {
  69. cin>>n>>m;
  70. for(int i=1;i<=n;i++)
  71. cin>>p[i],rt[i]=newt(1,n,p[i]),f[i]=i,id[p[i]]=i;
  72. id[0]=-1;
  73. //pp();
  74. for(int i=1;i<=m;i++)
  75. {
  76. cin>>u>>v;
  77. if(find(u)==find(v))continue;
  78. rt[find(u)]=merge(rt[find(u)],rt[find(v)],1,n);
  79. f[find(v)]=find(u);
  80. }
  81. cin>>q;
  82. while(q--)
  83. {
  84. //
  85. cin>>op;
  86. if(op=='Q')
  87. {
  88. cin>>x>>y;
  89. cout<<id[query(rt[find(x)],1,n,y)]<<endl;
  90. }
  91. if(op=='B')
  92. {
  93. cin>>u>>v;
  94. if(find(u)!=find(v))
  95. rt[find(u)]=merge(rt[find(u)],rt[find(v)],1,n),f[find(v)]=find(u);
  96. }
  97. }
  98.  
  99. return 0;
  100. }

后记

后来发现这道题还有更优的平衡树解法,但这个做法应该是码量最少的了,这也说明了值域线段树可以实现一些普通平衡树的操作,但不能完全替代。

P3224 [HNOI2012]永无乡 题解的更多相关文章

  1. 线段树合并+并查集 || BZOJ 2733: [HNOI2012]永无乡 || Luogu P3224 [HNOI2012]永无乡

    题面:P3224 [HNOI2012]永无乡 题解: 随便写写 代码: #include<cstdio> #include<cstring> #include<iostr ...

  2. 洛谷 P3224 [HNOI2012]永无乡 解题报告

    P3224 [HNOI2012]永无乡 题目描述 永无乡包含 \(n\) 座岛,编号从 \(1\) 到 \(n\) ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 \(n\) 座岛排名,名次用 ...

  3. bzoj2733 / P3224 [HNOI2012]永无乡(并查集+线段树合并)

    [HNOI2012]永无乡 每个联通块的点集用动态开点线段树维护 并查集维护图 合并时把线段树也合并就好了. #include<iostream> #include<cstdio&g ...

  4. 洛谷P3224 [HNOI2012]永无乡(线段树合并+并查集)

    题目描述 永无乡包含 nnn 座岛,编号从 111 到 nnn ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 nnn 座岛排名,名次用 111 到 nnn 来表示.某些岛之间由巨大的桥连接, ...

  5. 洛谷 P3224 [HNOI2012]永无乡

    题面 永无乡包含 \(n\) 座岛,编号从 \(1\) 到 \(n\) ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 \(n\) 座岛排名,名次用 \(1\) 到 \(n\) 来表示.某些岛 ...

  6. P3224 [HNOI2012]永无乡(平衡树合并)

    题目描述 永无乡包含 nn 座岛,编号从 11 到 nn ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 nn 座岛排名,名次用 11 到 nn 来表示.某些岛之间由巨大的桥连接,通过桥可以从 ...

  7. [洛谷P3224][HNOI2012]永无乡

    题目大意:给你$n$个点,每个点有权值$k$,现有两种操作: 1. $B\;x\;y:$将$x,y$所在联通块合并2. $Q\;x\;k:$查询第$x$个点所在联通块权值第$k$小是哪个数 题解:线段 ...

  8. P3224 [HNOI2012]永无乡

    思路 平衡树+启发式合并 貌似也可以线段树合并 连边就是合并两个Treap,查询就是第k大 使用Treap,好写好调 代码 #include <cstdio> #include <a ...

  9. 2018.08.11 洛谷P3224 [HNOI2012]永无乡(线段树合并)

    传送门 给出n个带点权的点,支持连边和查询连通块第k大. 这个貌似就是一道线段树合并的裸板啊... 代码: #include<bits/stdc++.h> #define N 100005 ...

随机推荐

  1. 关于Marshal 类的整理

    在两个不同的实体(两个线程或者进程甚至机器.在Managed和Unmanaged之间)进行方法调用和参数传递的时候,具体的调用方法和参数的内存格式可能需要一定的转换,这个转换的过程叫做Marshal. ...

  2. Mysql中不能update自身的解决方法

    不能执行:update bi_data.order_all_detailset err_msg='同时存在于wx,zfb平台',proc_time=now()where order_no in( se ...

  3. PC点击链接打开QQ聊天窗口

    <a target="_blank" href="http://wpa.qq.com/msgrd?v=3&uin=QQ号码&site=qq& ...

  4. maven打包 运行出现 错误: 找不到或无法加载主类 jar

    使用maven进行打包成jar包后 使用java -jar运行jar包 出现 错误: 找不到或无法加载主类 jar 主要是由于依赖没下载好,重新下载依赖 以及要在项目的pom.xml里面添加 < ...

  5. 逆波兰(非与或)表达式原理及C++代码实现

    p.p1 { margin: 0; font: 11px Menlo; color: rgba(209, 47, 27, 1); background-color: rgba(255, 255, 25 ...

  6. nim_duilib(13)之添加fmt库

    introduction 习惯使用fmt库做字符串的格式化操作.尽管nim_duilib提供了类似的函数. 故项目demo_xml引入了外部库fmt framework.h中添加下面的以便使用fmt库 ...

  7. 【LeetCode】723. Candy Crush 解题报告 (C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 暴力 日期 题目地址:https://leetcode ...

  8. 【LeetCode】112. 路径总和 Path Sum 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 DFS 回溯 BFS 栈 日期 题目地址:https ...

  9. 【LeetCode】516. Longest Palindromic Subsequence 最长回文子序列

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题思路 代码 刷题心得 日期 题目地址:https://le ...

  10. Codeforces A. Orchestra

    A. Orchestra time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...