[BZOJ2733][HNOI2010]永无乡 解题报告 启发式合并,线段树合并
好久没更新博客了,前段时间一直都在考试,都没时间些,现在终于有点闲了(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]永无乡 解题报告 启发式合并,线段树合并的更多相关文章
- BZOJ- 2733: 永无乡 (并查集&线段树合并)
题意:给定N个节点,K次操作,操作有两种,1是合并两个集合,2是求某个集合的第K大(从小到大排序). 思路:合并只要启发式即可.此题可以用线段树,保存1到N的排序的出现次数和. 复杂度O(NlogN) ...
- [BZOJ2733] [HNOI2012]永无乡(并查集 + 线段树合并)
传送门 一看到第k大就肯定要想到什么权值线段树,主席树,平衡树之类的 然后就简单了 用并查集判断连通,每个节点建立一颗权值线段树,连通的时候直接合并即可 查询时再二分递归地查找 时间复杂度好像不是很稳 ...
- bzoj2733 / P3224 [HNOI2012]永无乡(并查集+线段树合并)
[HNOI2012]永无乡 每个联通块的点集用动态开点线段树维护 并查集维护图 合并时把线段树也合并就好了. #include<iostream> #include<cstdio&g ...
- 洛谷 P3224 [HNOI2012]永无乡 解题报告
P3224 [HNOI2012]永无乡 题目描述 永无乡包含 \(n\) 座岛,编号从 \(1\) 到 \(n\) ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 \(n\) 座岛排名,名次用 ...
- bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)
这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例, ...
- [HNOI2012] 永无乡 解题报告 (splay+启发式合并)
题目链接:https://www.luogu.org/problemnew/show/P3224#sub 题目: 题目大意: 维护多个联通块,没有删除操作,每次询问某一联通块的第k大 解法: 维护联通 ...
- 【BZOJ2733】永无乡(线段树,并查集)
[BZOJ2733]永无乡(线段树,并查集) 题面 BZOJ 题解 线段树合并 线段树合并是一个很有趣的姿势 前置技能:动态开点线段树 具体实现:每次合并两棵线段树的时候,假设叫做\(t1,t2\), ...
- BZOJ2733 永无乡【splay启发式合并】
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- 【BZOJ-2733】永无乡 Splay+启发式合并
2733: [HNOI2012]永无乡 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2048 Solved: 1078[Submit][Statu ...
随机推荐
- 配置centos7来支持xshell远程访问和xftp传输文件
前提: 首先需要一台已装有centos7的电脑(虚拟机的配置这里不说明,这里用的是物理机) 背景: 在工作中访问linux的环境通常需要Xshell等终端软件,通过配置静态IP远程服务器进行管理开发. ...
- [学习笔记]编译sensetime发表的Single View Stereo Matching(SVS)遇到的问题
最近在研究用深度学习预测图像深度信息的方法,一开始用的是2017年CVPR上Godard大神的monodepth,代码在这里.这篇文章介绍了利用双目的consistency训练网络以对单张图像进行深度 ...
- Unity面试技巧之C#基础
1. 定义常量最好使用运行是常量就是readonly 编译常量就是 const public static readonly MyClass myClass = new MyClass(); publ ...
- 【LeetCode算法题库】Day1:TwoSums & Add Two Numbers & Longest Substring Without Repeating Characters
[Q1] Given an array of integers, return indices of the two numbers such that they add up to a specif ...
- LimeSDR在windows下使用Gqrx来接收FM广播
本文内容.开发板及配件仅限用于学校或科研院所开展科研实验! 淘宝店铺名称:开源SDR实验室 LimeSDR链接:https://item.taobao.com/item.htm?spm=a230r.1 ...
- 【Docker】第二篇 Docker镜像管理
一.搜索镜像 1.下载一个docker镜像:我们可以通过登陆docker网站搜索自己需要的镜像,可以选择自己所需要的版本,然后通过详情也可以看到:网址:https://hub.docker.com/2 ...
- linux 安装配置kafka脚本
安装脚本 #!/bin/bash # auto install kafka echo "========= Start to install kafka ==============&quo ...
- Mac下基于testrpc和truffle的以太坊智能合约开发环境搭建
原文地址:石匠的blog truffle是一个基于Javascript开发的一套智能合约开发框架,使用Solidity语言编写合约.truffle有一套自动的项目构建机制,集成了开发,测试和部署的各个 ...
- Spring的bean创建详解
IoC容器,又名控制反转,全称为Inverse of Control,其是Spring最为核心的一个组件,其他的组件如AOP,Spring事务等都是直接或间接的依赖于IoC容器的.本文主 ...
- 20172329 2018-2019-2 《Java软件结构与数据结构》实验二报告
20172329 2018-2019-2 <Java软件结构与数据结构>实验二报告 课程:<Java软件结构与数据结构> 班级: 1723 姓名: 王文彬 学号:2017232 ...