代码
treap模拟集合的功能
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef int pointer;//采用数组代替指针
const int maxn=10001*4;//结点个数
struct Treap{//树堆
struct Node{
pointer ch[2];//两个表示儿子的编号
int r,v;//r表示键值,v表示权值
int cmp(int x)const{//比较函数
if(x==v)return -1;//相同返回-1
return x<v?0:1;
//小于当前结点,返回0 表示这个值只可能在它的左子
//大于当前结点,返回1 表示这个值只可能在它的右子
}
}nodes[maxn];
int ncnt;pointer root;
Treap(){
root=ncnt=0;//初始化
}
void rotate(pointer& o,int d){//对结点o进行 左旋/右旋
//d表示旋转方向
//d=0表示"左旋",d=1表示右旋
pointer k=nodes[o].ch[d^1];
nodes[o].ch[d^1]=nodes[k].ch[d];
nodes[k].ch[d]=o;o=k;
/*
pointer k=nodes[o].ch[d^1];
swap(nodes[o],nodes[k]);
nodes[k].ch[d^1]=nodes[o].ch[d];
nodes[o].ch[d]=k;
*/
}
void insert(pointer& o,int x){//在以编号o为根的树中插入数值x
if(o==0){//如果当前结点不存在
o=++ncnt;//申请新的结点
nodes[o].ch[0]=nodes[o].ch[1]=0;
nodes[o].v=x;nodes[o].r=rand();//随机权值
}else{
int d=nodes[o].cmp(x);//判断在左子还是右子
if(d==-1){
return;//与当前结点权值相同,插入失败
}
insert(nodes[o].ch[d],x);//在对应的结点中插入x
if(nodes[nodes[o].ch[d]].r > nodes[o].r)
rotate(o,d^1);//如果根节点的权值较小,旋转
}
}
void insert(int x){//外接端口
insert(root,x);
}
void remove(pointer& o,int x){//在以编号o为根的树中删除数值x
if(o==0){//找到了叶子结点,也没有找到x值
return;//删除失败
}
int d=nodes[o].cmp(x);
if(d==-1){//如果x与当前根节点相等
if(nodes[o].ch[0]==0)//o的左子为空
o=nodes[o].ch[1];//用右子替代o
else if(nodes[o].ch[1]==0)//o的右子为空
o=nodes[o].ch[0];//用左子替代o
else{//两个子都为空
int d2=(nodes[nodes[o].ch[0]].r > nodes[nodes[o].ch[1]].r?1:0);
//如果左子权值大,右旋;如果右子权值大,左旋;
rotate(o,d2);remove(nodes[o].ch[d2],x);
//然后递归删除对应的子结点
}
}else
remove(nodes[o].ch[d],x);//在对应的子结点中删除
}
void remove(int x){//外接端口
remove(root,x);
}
bool find(pointer o,int x){
while(o!=0){
int d=nodes[o].cmp(x);//判断x值在左子还是右子
if(d==-1)return 1;//返回找到了
else o=nodes[o].ch[d];//否则,在对应子树继续查找
}
return 0;//没找到
}
bool find(int x){//外接端口
return find(root,x);
}
}trp;
int main(){
trp.insert(1);
trp.insert(2);
trp.insert(3);
printf("%d %d %d\n",trp.find(1),trp.find(2),trp.find(3));
trp.remove(1);
trp.remove(2);
trp.remove(3);
printf("%d %d %d\n",trp.find(1),trp.find(2),trp.find(3));
return 0;
}
treap实现名次树
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef int pointer;//采用数组代替指针
const int maxn=10001*4;//结点个数
struct Treap{//树堆(名次树)
struct Node{
pointer ch[2];//两个表示儿子的编号
int r,v,s;//r表示键值,v表示权值
//s表示以当前点为根节点的子树的节点个数
int cmp(int x)const{//比较函数
if(x==v)return -1;//相同返回-1
return x<v?0:1;
//小于当前结点,返回0 表示这个值只可能在它的左子
//大于当前结点,返回1 表示这个值只可能在它的右子
}
}nodes[maxn];
void maintain(pointer o){//维护结点size的正确性
nodes[o].s=nodes[nodes[o].ch[0]].s+nodes[nodes[o].ch[1]].s+1;
}
//根结点size=左子size+右子size+1
//特别的:NULL(即0号结点)的size=0
int ncnt;pointer root;
Treap(){
root=ncnt=0;//初始化
}
void rotate(pointer& o,int d){//对结点o进行 左旋/右旋
//d表示旋转方向
//d=0表示"左旋",d=1表示右旋
pointer k=nodes[o].ch[d^1];
nodes[o].ch[d^1]=nodes[k].ch[d];
nodes[k].ch[d]=o;
maintain(o);maintain(k);
o=k;
/*
pointer k=nodes[o].ch[d^1];
swap(nodes[o],nodes[k]);
nodes[k].ch[d^1]=nodes[o].ch[d];
nodes[o].ch[d]=k;
*/
}
void insert(pointer& o,int x){//在以编号o为根的树中插入数值x
if(o==0){//如果当前结点不存在
o=++ncnt;//申请新的结点
nodes[o].ch[0]=nodes[o].ch[1]=0;
nodes[o].v=x;nodes[o].r=rand();//随机权值
}else{
int d=nodes[o].cmp(x);//判断在左子还是右子
if(d==-1){
return;//与当前结点权值相同,插入失败
}
insert(nodes[o].ch[d],x);//在对应的结点中插入x
if(nodes[nodes[o].ch[d]].r > nodes[o].r)
rotate(o,d^1);//如果根节点的权值较小,旋转
}
maintain(o);
}
void insert(int x){//外接端口
insert(root,x);
}
void remove(pointer& o,int x){//在以编号o为根的树中删除数值x
if(o==0){//找到了叶子结点,也没有找到x值
return;//删除失败
}
int d=nodes[o].cmp(x);
if(d==-1){//如果x与当前根节点相等
if(nodes[o].ch[0]==0)//o的左子为空
o=nodes[o].ch[1];//用右子替代o
else if(nodes[o].ch[1]==0)//o的右子为空
o=nodes[o].ch[0];//用左子替代o
else{//两个子都为空
int d2=(nodes[nodes[o].ch[0]].r > nodes[nodes[o].ch[1]].r?1:0);
//如果左子权值大,右旋;如果右子权值大,左旋;
rotate(o,d2);remove(nodes[o].ch[d2],x);
//然后递归删除对应的子结点
}
}else
remove(nodes[o].ch[d],x);//在对应的子结点中删除
if(o!=0)maintain(o);//应要注意o!=0
}
void remove(int x){//外接端口
remove(root,x);
}
bool find(pointer o,int x){
while(o!=0){
int d=nodes[o].cmp(x);//判断x值在左子还是右子
if(d==-1)return 1;//返回找到了
else o=nodes[o].ch[d];//否则,在对应子树继续查找
}
return 0;//没找到
}
bool find(int x){//外接端口
return find(root,x);
}
int kth(pointer o,int k){//查询第k大元素
if(o==0 || k<=0 || k> nodes[o].s)return 0;//查询失败
int s=(nodes[o].ch[1]==0 ?0:nodes[nodes[o].ch[1]].s);//计算右子大小
if(k==s+1)return nodes[o].v;//恰为当前结点
else if(k<=s)return kth(nodes[o].ch[1],k);//在左子递归寻找
else return kth(nodes[o].ch[0],k-s-1);//在右子递归寻找
}
int kth(int k){//外接端口
return kth(root,k);
}
}trp;
int main(){
trp.insert(1);
trp.insert(2);
trp.insert(3);
printf("%d %d %d\n",trp.find(1),trp.find(2),trp.find(3));
printf("%d %d %d\n",trp.kth(3),trp.kth(2),trp.kth(1));
return 0;
}
未完待续
代码仍在调试阶段,讲解暂无,未完待续…
2017.8.12 goseqh 评论 erverlasting 写的隔膜中,人物瞬移的问题:It’s not a bug, it’s a feature!
承接上文
[2017.12.23]在四个月后的今天,我终于第一次写出了我自己的treap,好开心,再此对拍程序。
treap代码 treap.cpp
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=1000000+10;
namespace Treap{
int ch[maxn][2],k[maxn],r[maxn],siz[maxn],ncnt;
int Root=0;void init(){Root=0;ncnt=0;}
int _cmp(int t,int x){
if(k[t]==x)return -1;
return x < k[t] ? 0 : 1;
}
void maintain(int t){
siz[t]=siz[ch[t][0]]+siz[ch[t][1]]+1;
}
void rotate(int& t,int d){
int k=ch[t][d^1];
ch[t][d^1]=ch[k][d];ch[k][d]=t;
maintain(t);t=k;maintain(k);
}
void insert(int& t,int x){
if(t==0){
t=++ncnt;ch[t][0]=ch[t][1]=0;
k[t]=x;r[t]=rand();siz[t]=1;
}else{
int d=_cmp(t,x);if(d==-1)return;
insert(ch[t][d],x);maintain(t);
if(r[t]<r[ch[t][d]])rotate(t,d^1);
}
}
void erase(int& t,int x){
if(t==0)return; int d=_cmp(t,x);
if(d==-1){
if(ch[t][0]==0 && ch[t][1]==0)t=0;
else if(ch[t][0]==0 || ch[t][1]==0)
t=ch[t][0]+ch[t][1];
else{
rotate(t,0);erase(ch[t][0],x);
maintain(t);
}
}else erase(ch[t][d],x),maintain(t);
}
int rank(int t,int x){
if(t==0)return 1; int d=_cmp(t,x);
if(d==-1)return siz[ch[t][0]]+1;
int ans=rank(ch[t][d],x);
return d==1 ? ans+siz[ch[t][0]]+1 : ans;
}
int _comp(int x,int y){
if(x==y)return -1;
return x>y ? 0 : 1;
}
int xth(int t,int x){
if(t==0)return x<=0 ? -0x7f7f7f7f : 0x7f7f7f7f;
int d=_comp(siz[ch[t][0]]+1,x);
if(d==-1)return k[t];
if(d== 1)x-=siz[ch[t][0]]+1;
return xth(ch[t][d],x);
}
void insert(int x){ insert(Root,x);}
void erase (int x){ erase(Root,x);}
int rank (int x){return rank(Root,x);}
int xth (int x){return xth(Root,x);}
}
#include<cctype>
int geti(){
int ans=0;int flag=0;char c=getchar();
while(!isdigit(c)){flag|=c=='-';c=getchar();}
while( isdigit(c)){ans=ans*10+c-'0';c=getchar();}
return flag?-ans:ans;
}
template<class T>inline void puti(T x){
if(x<0)x=-x, putchar('-');
if(x>9)puti(x/10); putchar(x%10+'0');
}
int main(){
int q=geti();//instructions
while(q--){
int op=geti(),x=geti();
if(op==1) Treap::insert(x);//insert
else if(op==2) Treap::erase(x);//erase
else if(op==3)puti(Treap::rank(x)),putchar('\n');//rank
else if(op==4) puti(Treap::xth(x)),putchar('\n');//xth
}
return 0;
}
暴力代码 bforce.cpp
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=1000000+10;bool exist[maxn];
int rank(int x){
int ans=0;
for(int i=1;i<x;i++)ans+=exist[i];
return ans+1;
}
int xth(int x){
for(int i=1;x;i++){
x-=exist[i];if(x==0)return i;
}
}
int main(){
int q;scanf("%d",&q);
while(q--){
int op,x;scanf("%d%d",&op,&x);
if(op==1)exist[x]=1;
else if(op==2)exist[x]=0;
else if(op==3)printf("%d\n",rank(x));
else if(op==4) printf("%d\n",xth(x));
}
return 0;
}
数据生成器 data.cpp
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<ctime>
using namespace std;
int rand(int L,int R){return rand()%(R-L+1)+L;}
bool exist[1000000+10];int cnt=0;
int main(){
srand(time(NULL));
int n=rand(50,100);printf("%d\n",n);
while(cnt<30){
int x=rand(1,1000);
if(exist[x])continue;
exist[x]=1;cnt++;
printf("1 %d\n",x);
}
while(cnt>20){//erase
for(int j=1;j<=1000 && cnt>20;j++){
if(exist[j] && (rand()&1)){
cnt--;exist[j]=0;printf("2 %d\n",j);
}
}
}
for(int i=41;i<=n;i++){
int op=rand(3,4);
if(op==3){
int x;
while(1)for(int i=1;i<=1000;i++)
if(exist[i] && (rand()&1)){
x=i;goto outside;
}
outside:printf("3 %d\n",x);
}else if(op==4){
int x=rand(1,cnt);
printf("4 %d\n",x);
}
}
return 0;
}
windows下的batch对拍 try.bat
@echo off
set /a i=1
:begin
if %i% GTR 100 goto end
echo round %i% ...
data.exe > input.txt
treap.exe < input.txt > output.txt
bforce.exe < input.txt > std.txt
fc output.txt std.txt
if errorlevel 1 pause
set /a i=i+1
goto begin
:end
echo end!
pause
即使本程序已经过多次对拍,如有谬误,敬请谅解。
附赠头文件 Treap.hpp
重要的是吐槽的内容,指出了一些常见的错误。
#pragma once //这是 GGN 在 2017.12.23 写得一个小头文件
#include<cstdlib> //这些注释标明了一些容易写错的地方,以及一些吐槽
#ifndef maxn //你可以在include之前define一个maxn,以实现对内存大小的控制
#define maxn (1000000+10) // GGN 指针用得挺好的,但是更喜欢用数组存树...
#endif
namespace std{ //你要像用stl一样写上 "using namespace std;"
class Treap{ //我懒得把这个类写成模板了...
int ch[maxn][2],k[maxn],r[maxn],siz[maxn],ncnt; //懒得定义结构体存结点
int Root; //这是树的根
int _cmp(int t,int x){
if(k[t]==x)return -1;
return x < k[t] ? 0 : 1;
}
void maintain(int t){ //在涉及元素插入删除,子树交换时一定要【maintain】
siz[t]=siz[ch[t][0]]+siz[ch[t][1]]+1;//这里应该记得 "+1"
}
void rotate(int& t,int d){//treap的核心思想就是rotate
int k=ch[t][d^1];
ch[t][d^1]=ch[k][d];ch[k][d]=t;//交换顺序逻辑性其实很强
maintain(t);t=k;maintain(k);//注意maintain的顺序
}
void insert(int& t,int x){//insert/erase/rotate 传引用,其余不用
if(t==0){
t=++ncnt;ch[t][0]=ch[t][1]=0;//申请新的节点要注意清零
k[t]=x;r[t]=rand();siz[t]=1;//记得将siz设成1
}else{
int d=_cmp(t,x);if(d==-1)return;
insert(ch[t][d],x);maintain(t);//记得maintain
if(r[t]<r[ch[t][d]])rotate(t,d^1);//此处不用maintain
}
}
void erase(int& t,int x){//在涉及树形态变化时,要传引用,反之不用
if(t==0)return; int d=_cmp(t,x);
if(d==-1){
if(ch[t][0]==0 && ch[t][1]==0)t=0;//直接砍掉,丢到内存里就好了
else if(ch[t][0]==0 || ch[t][1]==0)
t=ch[t][0]+ch[t][1];//简洁明了
else{
rotate(t,0);erase(ch[t][0],x);//左旋,右旋其实都可以
maintain(t);//此处需要maintain
}
}else erase(ch[t][d],x),maintain(t);//maintain
}
int rank(int t,int x){
if(t==0)return 1; int d=_cmp(t,x);
if(d==-1)return siz[ch[t][0]]+1;
int ans=rank(ch[t][d],x);
return d==1 ? ans+siz[ch[t][0]]+1 : ans;//注意哪个加,哪个不加
}
int _comp(int x,int y){
if(x==y)return -1;
return x>y ? 0 : 1;//comp和cmp的定义左右大小正好是烦的
}
int xth(int t,int x){
if(t==0)return x<=0 ? -0x7f7f7f7f : 0x7f7f7f7f;//玄学...
int d=_comp(siz[ch[t][0]]+1,x);
if(d==-1)return k[t];
if(d== 1)x-=siz[ch[t][0]]+1;//注意哪个减,哪个不减
return xth(ch[t][d],x);
}
public:
void init(){Root=0;ncnt=0;}
Treap(){init();}
void insert(int x){ insert(Root,x);}
void erase (int x){ erase(Root,x);if(Root==0)init();}//节约空间
int rank (int x){return rank(Root,x);}
int xth (int x){return xth(Root,x);}
bool empty () {return Root==0;}
int size () {return siz[Root];}
};
}
#undef maxn