好久没更新博客了,前段时间一直都在考试,都没时间些,现在终于有点闲了(cai guai)...

  写了一道题,[HNOI2012]永无乡,其实是一道板子题,我发现我写了好多板子题...还是太菜了...

  这道题有两个操作,合并和查询第k小,合并可以用到启发式合并,查询是平衡树,我这里写的是Splay+启发式合并.

  启发式合,其实就是暴力合并,做法是将要合并的两个节点分别旋转到根,再把size小的拆掉,暴力插入到另一棵树里.这样做的复杂度是O(logn(拆散一棵树)*logn(插入另一棵树)),即O(logn^2).

  

#include<iostream>
#include<cstdio>
#include<cstdlib>
#define inf (1<<30)
#define maxn (200010)
#define il inline
#define RG register
using namespace std;
il int gi(){ RG int x=0,q=1; RG char ch=getchar(); while( ( ch<'0' || ch>'9' ) && ch!='-' ) ch=getchar();
  if( ch=='-' ) q=-1,ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; }

int n,m,Q,Fa[maxn],w[maxn],que[maxn];
int size[maxn],ls[maxn],rs[maxn],fa[maxn];
il int find(int x){return Fa[x]==x?x:Fa[x]=find(Fa[x]);}

il void up(int x){ if(!x) return ; size[x]=size[ls[x]]+size[rs[x]]+1; }

il void rotate(int x){
  RG int y=fa[x],z=fa[y];
  bool v1=(x==ls[y]);
  if(z) if(y==ls[z]) ls[z]=x; else rs[z]=x;
  fa[x]=z,fa[y]=x;
  if(v1) fa[rs[x]]=y,ls[y]=rs[x],rs[x]=y;
  else fa[ls[x]]=y,rs[y]=ls[x],ls[x]=y;
  up(y);
}

il void Splay(int x){
  while(fa[x]){
    RG int y=fa[x],z=fa[y];
    if(z) if((x==ls[y])^(y==ls[z])) rotate(x);
      else rotate(y);
    rotate(x);
  }
  up(x);
}

il void Insert(RG int &x,RG int f,RG int y){
  if(!x){ x=y,fa[x]=f,size[x]=1,Splay(x); return ; }
  if(w[y]<=w[x]) Insert(ls[x],x,y);
  else Insert(rs[x],x,y);
  up(x);
}

il void Merge(int x,int y){
  Splay(x),Splay(y);
  if(size[x]>size[y]) swap(x,y);
  RG int hd=0,tl=1; que[0]=y,que[1]=x;
  while(hd<tl){
    RG int cur=que[++hd];
    if(ls[cur]) que[++tl]=ls[cur];
    if(rs[cur]) que[++tl]=rs[cur];
    ls[cur]=rs[cur]=0;
    Insert(que[hd-1],0,cur);
  }
}

il void Query(RG int x,RG int k){
  if(k>size[x]){ printf("-1\n"); return ; }
  if(k==size[ls[x]]+1){ printf("%d\n",x); return ; }
  else if(k<size[ls[x]]+1){ Query(ls[x],k); return ; }
  else{ Query(rs[x],k-size[ls[x]]-1); return ; }
}

il void init(){
  n=gi(),m=gi(); int u,v;
  for(RG int i=1;i<=n;i++) w[i]=gi(),Fa[i]=i,size[i]=1;
  for(RG int i=1;i<=m;i++){
    u=gi(),v=gi(); RG int f1=find(u),f2=find(v);
    if(f1!=f2) Merge(u,v),Fa[f1]=f2;
  }
}

il void work(){
  Q=gi(); int x,y; char s[5];
  for(RG int i=1;i<=Q;i++){
    scanf("%s",s); x=gi(),y=gi();
    if(s[0]=='B'){ RG int f1=find(x),f2=find(y);
      if(f1!=f2) Merge(x,y),Fa[f1]=f1; }
    else Splay(x),Query(x,y);
  }
}

int main(){ init(); work(); return 0; }

  

  对于这道题,还有一种方法,线段树合并,速度比Splay+启发式合并快,代码也比较短.

  因为代码非常好理解,所以不再赘述,直接上代码.

#include<iostream>
#include<cstdio>
#include<cstdlib>
#define inf (1<<30)
#define maxn (2000010)
#define ll long long
#define il inline
#define RG register
using namespace std;
il int gi(){ RG int x=0,q=1; RG char ch=getchar(); while( ( ch<'0' || ch>'9' ) && ch!='-' ) ch=getchar();
  if( ch=='-' ) q=-1,ch=getchar(); while(ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; }

int n,m,Q,cnt;
int w[maxn],pos[maxn],fa[maxn],rt[maxn];
int ls[maxn],rs[maxn],size[maxn];

int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); }

void insert(RG int &x,RG int l,RG int r,RG int v){
  if(!x) x=++cnt;
  if(l==r){ size[x]=1; return; }
  int mid=(l+r)>>1;
  if(v<=mid) insert(ls[x],l,mid,v);
  else insert(rs[x],mid+1,r,v);
  size[x]=size[ls[x]]+size[rs[x]];
}

int query(RG int x,RG int l,RG int r,RG int k){
  if(l==r) return l;
  int mid=(l+r)>>1;
  if(size[ls[x]]>=k) return query(ls[x],l,mid,k);
  else return query(rs[x],mid+1,r,k-size[ls[x]]);
}

int Merge(RG int x,RG int y){
  if(!x || !y) return x+y;
  ls[x]=Merge(ls[x],ls[y]);
  rs[x]=Merge(rs[x],rs[y]);
  size[x]=size[ls[x]]+size[rs[x]];
  return x;
}

il void init(){
  n=gi(),m=gi(); int u,v,f1,f2;
  for(RG int i=1;i<=n;i++) w[i]=gi();
  for(RG int i=1;i<=n;i++) fa[i]=i;
  for(RG int i=1;i<=m;i++){ u=gi(),v=gi();
    f1=find(u),f2=find(v); fa[f1]=f2; }
  for(RG int i=1;i<=n;i++){
    insert(rt[find(i)],1,n,w[i]);
    pos[w[i]]=i;
  }
}

il void work(){
  Q=gi(); int x,y,f1,f2; char s[5];
  for(RG int i=1;i<=Q;i++){
    scanf("%s",s);
    x=gi(),y=gi();
    if(s[0]=='Q'){
      f1=find(x);
      if(size[rt[f1]]<y){ printf("-1\n"); continue; }
      printf("%d\n",pos[query(rt[f1],1,n,y)]);
    }
    else{ f1=find(x),f2=find(y);
      if(f1!=f2) fa[f1]=f2,rt[f2]=Merge(rt[f1],rt[f2]); }
  }
}

int main(){ init(); work(); return 0; }

  

  最后祝大家切题愉快!!!

[BZOJ2733][HNOI2010]永无乡 解题报告 启发式合并,线段树合并的更多相关文章

  1. BZOJ- 2733: 永无乡 (并查集&线段树合并)

    题意:给定N个节点,K次操作,操作有两种,1是合并两个集合,2是求某个集合的第K大(从小到大排序). 思路:合并只要启发式即可.此题可以用线段树,保存1到N的排序的出现次数和. 复杂度O(NlogN) ...

  2. [BZOJ2733] [HNOI2012]永无乡(并查集 + 线段树合并)

    传送门 一看到第k大就肯定要想到什么权值线段树,主席树,平衡树之类的 然后就简单了 用并查集判断连通,每个节点建立一颗权值线段树,连通的时候直接合并即可 查询时再二分递归地查找 时间复杂度好像不是很稳 ...

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

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

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

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

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

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

  6. [HNOI2012] 永无乡 解题报告 (splay+启发式合并)

    题目链接:https://www.luogu.org/problemnew/show/P3224#sub 题目: 题目大意: 维护多个联通块,没有删除操作,每次询问某一联通块的第k大 解法: 维护联通 ...

  7. 【BZOJ2733】永无乡(线段树,并查集)

    [BZOJ2733]永无乡(线段树,并查集) 题面 BZOJ 题解 线段树合并 线段树合并是一个很有趣的姿势 前置技能:动态开点线段树 具体实现:每次合并两棵线段树的时候,假设叫做\(t1,t2\), ...

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

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

  9. 【BZOJ-2733】永无乡 Splay+启发式合并

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

随机推荐

  1. Android #Android开发环境搭建

    Android #Android开发环境搭建 1.下载:Google在国服的官网 https://developer.android.google.cn/index.html 1.点击首页 “ 获取 ...

  2. opengl矩阵向量

    如何创建一个物体.着色.加入纹理,给它们一些细节的表现,但因为它们都还是静态的物体,仍是不够有趣.我们可以尝试着在每一帧改变物体的顶点并且重配置缓冲区从而使它们移动,但这太繁琐了,而且会消耗很多的处理 ...

  3. Controller组件- 集合点的功能-loadrunner

    1.添加集合点功能的做法 ,注意在开始事务前加,不然就会把等待时间也加进去. 2.Controller 中也要开启集合点的功能,才能使用  

  4. Java中 static、final和static final的特点及区别

    final: final可以修饰:属性,方法,类,局部变量(方法中的变量) final修饰的属性的初始化可以在编译期,也可以在运行时,初始化后不能被改变. final修饰的属性跟具体对象有关,在运行期 ...

  5. Streamr助你掌控自己的数据

    博客说明 所有刊发内容均可转载但是需要注明出处. 项目简介 Streamr 致力于为世界实时数据的自由公平交换打造开源平台,并促进全球数据经济的发展.Streamr项目基于区块链技术,并向用户提供数据 ...

  6. mysql 连接超时解决方案: 怎样修改默认超时时间

    mysql数据库有一个wait_timeout的配置,默认值为28800(即8小时). 在默认配置不改变的情况下,如果连续8小时内都没有访问数据库的操作,再次访问mysql数据库的时候,mysql数据 ...

  7. can总线实现stm32的IAP

    使用stm32f105rct6的can通信做IAP,实现固件的远程更新功能.IAP的实现包括两个程序:BootLoader和应用程序.启动过程先启动BootLoader,等待1s,若接收到烧写指令则开 ...

  8. pssh命令详解

    基础命令学习目录首页 原文链接:https://www.cnblogs.com/kevingrace/p/6378719.html pssh提供OpenSSH和相关工具的并行版本.包括pssh,psc ...

  9. 互评Beta版本 - Hello World团队项目空天猎

    由于改组项目未提供可以直接进行安装运行的安装包或可执行文件,所以我找到了该组组长陈同学,由他根据其小组项目的功能说明书进行演示. 基于NABCD评论作品,及改进建议 每个小组评论其他小组beta发布的 ...

  10. Scrum Meeting 10.28

    今天大部分同学仍停留在学习阶段,进度快的同学已经在配置SQLserver. 成员 今日完成任务 明日计划 所用时间 徐越 配置SQLserver,试用java程序连接数据库 学习servlet,htt ...