题目类型:平衡树启发式合并

传送门:>Here<

题意:节点可以连边(不能断边),询问任意两个节点的连通性与一个连通块中排名第\(k\)的节点

解题思路

如果不需要询问排名,那么并查集即可。如果只询问排名第一,那么左偏树即可。现在要询问排名第\(k\)小,就需要用平衡树来解决

平衡树求解排名第\(k\)是轻而易举的,然而怎么合并两棵平衡树呢?

启发式合并。所谓启发式合并,就是暴力合并……

所谓启发式合并(不仅仅是平衡树),就是比较要合并的两个结构,选择较小的那一个结构,将其中节点一个一个拆下来插入到较大的那个结构中去。因此当我们合并两棵平衡树时,将\(size\)较小的那一颗平衡树中的节点一个一个拆下来插入到较大的那棵平衡树上。

按什么顺序拆呢?如果每次选择根节点删除然后插入显得很愚蠢。我们尽可能优化删除的情况(插入不可能优化了吧……)——按照后序遍历的顺序来插入。这样的话当我插入一个节点时,它的左右子树肯定都已经没了,因此只需要简单地将自己删除就好了。

Code

/*By DennyQi 2018*/
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#define r read()
using namespace std;
typedef long long ll;
const int MAXN = 100010;
const int INF = 1061109567;
inline int Max(const int a, const int b){ return (a > b) ? a : b; }
inline int Min(const int a, const int b){ return (a < b) ? a : b; }
inline int read(){
int x = 0; int w = 1; register char c = getchar();
for(; c ^ '-' && (c < '0' || c > '9'); c = getchar());
if(c == '-') w = -1, c = getchar();
for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w;
}
int N,M,q,A,B,x,y; char opt[10];
int val[MAXN],bel[MAXN],rt[MAXN];
struct Splay{
int ch[MAXN][2],fa[MAXN],size[MAXN];
inline bool rson(int f, int x){
return ch[f][1] == x;
}
inline void update(int x){
size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}
inline void rotate(int x){
int f = fa[x], gf = fa[f];
int p = rson(f, x), q = !p;
if(!gf) rt[bel[x]] = x; else ch[gf][rson(gf,f)] = x;
fa[x] = gf;
ch[f][p] = ch[x][q], fa[ch[x][q]] = f;
ch[x][q] = f, fa[f] = x;
update(f), update(x);
}
inline void splay(int x, int target){
int f,gf;
while(fa[x] != target){
f = fa[x], gf = fa[f];
if(gf == target){
rotate(x); break;
}
if(rson(gf,f) ^ rson(f,x)) rotate(x); else rotate(f);
rotate(x);
}
}
inline void insert(int A, int x){
int o = rt[A];
while(o){
bool b = val[x] > val[o];
if(!ch[o][b]){
ch[o][b] = x;
fa[x] = o;
bel[x] = A;
size[x] = 1;
splay(x, 0);
break;
}
o = ch[o][b];
}
}
void HEmerge(int A, int x){
if(ch[x][0]) HEmerge(A, ch[x][0]);
if(ch[x][1]) HEmerge(A, ch[x][1]);
insert(A, x);
}
inline void merge(int A, int B){
if(size[rt[A]] < size[rt[B]]) swap(A, B);
HEmerge(A, rt[B]);
rt[B] = 0;
}
inline int query(int A, int k){
int o = rt[A];
if(size[o] < k) return -1;
while(o){
if(size[ch[o][0]] >= k){
o = ch[o][0];
}
else if(size[ch[o][0]] + 1 < k){
k -= size[ch[o][0]] + 1;
o = ch[o][1];
}
else{
return o;
}
}
}
}qxz;
int main(){
N = r, M = r;
for(int i = 1; i <= N; ++i){
val[i] = r;
rt[i] = i;
bel[i] = i;
qxz.size[i] = 1;
}
for(int i = 1; i <= M; ++i){
A = r, B = r;
if(bel[A] != bel[B]) qxz.merge(bel[A], bel[B]);
}
scanf("%d", &q);
while(q--){
scanf("%s %d %d", opt, &x, &y);
if(opt[0] == 'B'){
if(bel[x] == bel[y]) continue;
qxz.merge(bel[x], bel[y]);
}
else{
printf("%d\n", qxz.query(bel[x], y));
}
}
return 0;
}

☆ [HNOI2012] 永无乡 「平衡树启发式合并」的更多相关文章

  1. Bzoj 2733: [HNOI2012]永无乡 数组Splay+启发式合并

    2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3955  Solved: 2112[Submit][Statu ...

  2. Bzoj 2733: [HNOI2012]永无乡(线段树+启发式合并)

    2733: [HNOI2012]永无乡 Time Limit: 10 Sec Memory Limit: 128 MB Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己 ...

  3. bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)

    这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例, ...

  4. [BZOJ2733][HNOI2010]永无乡 解题报告 启发式合并,线段树合并

    好久没更新博客了,前段时间一直都在考试,都没时间些,现在终于有点闲了(cai guai)... 写了一道题,[HNOI2012]永无乡,其实是一道板子题,我发现我写了好多板子题...还是太菜了... ...

  5. BZOJ2733 永无乡 【splay启发式合并】

    2733: [HNOI2012]永无乡 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 4190  Solved: 2226 [Submit][Sta ...

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

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

  7. BZOJ2733 永无乡【splay启发式合并】

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  8. 2733. [HNOI2012]永无乡【平衡树-splay】

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

  9. BZOJ2733 [HNOI2012]永无乡 【线段树合并】

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

随机推荐

  1. ccflow表机构与运行机制(二次开发必看)

    驰骋工作流引擎,工作流程管理系统,表结构与运行机制. ------------------------------------------------------- 前言: 1, ccflow 有自动 ...

  2. cesium 之核心类 Viewer 简介篇

    前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材. 简 ...

  3. Numpy常用概念-对象的副本和视图、向量化、广播机制

    一.引言 在我们操作数组的时候,返回的是新数组还是原数组的链接,我们就需要了解对象副本和视图的区别. 向量化和广播是numpy内部实现的基础. 二.对象副本和视图 我们应该注意到,在操作数组的时候返回 ...

  4. iOS pthread

    pthread 是属于 POSIX 多线程开发框架 创建线程的方法:pthread_create   参数含义: 1.指向线程代号的指针 2.线程的属性 3.指向函数的指针 4.传递给该函数的参数 返 ...

  5. PHP面向对象的小总结

     面向对象特性: *重用性 (每个模块都可以在项目中重复使用) *灵活性 (每个模块都很轻松被替换更改的) *拓展性(在模块上添加新功能是很方便的) 类和对象的关系(类生成对象) 物以类聚:相同特性的 ...

  6. ubuntu16.04系统深度学习开发环境、常用软件环境(如vscode、wine QQ、 360wifi驱动(第三代暂无))搭建相关资料

    事后补充比较全面的(找对资料真的省一半功夫):https://www.jianshu.com/p/5b708817f5d8?from=groupmessage Ubuntu16.04 + 1080Ti ...

  7. centosFTP服务搭建及权限配置

    引用一个其他大佬对vsftpd的描述: vsftpd 是“very secure FTP daemon”的缩写,安全性是它的一个最大的特点. vsftpd 是一个 UNIX 类操作系统上运行的服务器的 ...

  8. 一、Windows Server 2016 AD服务器搭建

    简介: AD是Active Directory的简写,中文称活动目录.活动目录(Active Directory)主要提供以下功能: 1)服务器及客户端计算机管理 2)用户服务 3)资源管理 4)桌面 ...

  9. Vector与ArrayList区别

    1)Vector的方法都是同步的(Synchronized),是线程安全的: ArrayList的方法是线程不安全的. 由于线程同步必然会影响性能,因此,ArrayList的性能比Vector好. 请 ...

  10. 新建swap分区的规划、挂载和自动挂载示例

    注:来自Linux系统管理_磁盘分区和格式化的扩展 Linux系统管理_磁盘分区和格式化:http://murongqingqqq.blog.51cto.com/2902694/1361918 思路: ...