P6136 【模板】普通平衡树(数据加强版)

P6136 【模板】普通平衡树(数据加强版)

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些整数,其中需要提供以下操作:

  1. 插入一个整数 x
  2. 删除一个整数 x(若有多个相同的数,只删除一个)。
  3. 查询整数 x 的排名(排名定义为比当前数小的数的个数 +1)。
  4. 查询排名为 x 的数(如果不存在,则认为是排名小于 x 的最大数。保证 x 不会超过当前数据结构中数的总数)。
  5. x 的前驱(前驱定义为小于 x,且最大的数)。
  6. x 的后继(后继定义为大于 x,且最小的数)。

本题强制在线,保证所有操作合法(操作 2 保证存在至少一个 x,操作 4,5,6 保证存在答案)。

输入格式

第一行两个正整数 n,m,表示初始数的个数和操作的个数。

第二行 n 个整数 a1,a2,a3,,an,表示初始的数

接下来 m 行,每行有两个整数 optxopt 表示操作的序号($ 1 \leq \text{opt} \leq 6 x'$ 表示加密后的操作数。

我们记 last 表示上一次 3,4,5,6 操作的答案,则每次操作的 x 都要异或last 才是真实的 x。初始 last0

输出格式

输出一行一个整数,表示所有 3,4,5,6 操作的答案的异或和

限制与约定

对于 100% 的数据,1n1051m1060ai,x<230

因为
我调了一下午spaly没调出来
我实在是太想学FHQ-Treap了
所以这是一篇FHQ-Treap的总结:

FHQ-Treap只需要支持两种操作:分裂和合并
所以十分的好写(或许)

首先介绍一下FHQ-Treap需要的数组:
siz ; val ; pri: 分别表示以 x 为根的子树的大小,节点 x 的值,节点 x 的优先级(由 rand() 产生,其目的在维持树的平衡)
ch[N][2]: 节点x的左右儿子的位置(感觉有点像 0/1Tried 的写法)

分裂:

split(int x,int k) 表示将以x为根节点的树分裂成两颗树a,b
(x中的节点按照与k的大小关系被分入a,b中)

Code:

pi split(int x,int k)
{
if(!x)return pi(0,0);
if(val[x]<k)//完全在右子树
{
pi y=split(ch[x][1],k);
ch[x][1]=y.a;//将分裂后的左子树与x合并(k不在y.a中)
upd(x);
return pi(x,y.b);//最后分裂出了 x y.b 返回这两棵树
}
else//存在于左子树内
{
pi y= split(ch[x][0],k);
ch[x][0]=y.b;
upd(x);
return pi(y.a,x);
}
}

合并:

int merge(int x,int y) 表示将两棵树x,y合并将新的根节点返回

Code:

int merge(int x,int y)
{
if(!x||!y)return x+y;
if(pri[x]<pri[y])//有点左偏树的味道
{
ch[x][1]=merge(ch[x][1],y);
upd(x);
return x;
}
else
{
ch[y][0]=merge(x,ch[y][0]);//始终满足y子树内任意值比x大
upd(y);
return y;
}
}

插入:

先新建一个节点表示k,其下标为++cnt
insert(int k):
将rt按照k拆成两棵树x,y
然后合并merge(merge(x,cnt),y)

void insert(int k)
{
val[++cnt]=k;pri[cnt]=rand()*5+rand();siz[cnt]=1;
pi x=split(rt,k);
rt=merge(merge(x.a,cnt),x.b);
}

删除:

void del(int k)
{
pi x,y;
x=split(rt,k);//x.a:[0,k-1] x.b:[k,inf]
y=split(x.b,k+1);//y.a[k,k] y.b:[k+1,inf]
y.a=merge(ch[y.a][0],ch[y.a][1]);
x.b=merge(y.a,y.b);
rt=merge(x.a,x.b);
}

查询系列操作:

int Rank(int k)
{
int res=0;
pi x=split(rt,k);
res=siz[x.a]+1;
merge(x.a,x.b);
return res;
}
int kth(int x,int k)
{
if(k==siz[ch[x][0]]+1)return val[x];
if(k<=siz[ch[x][0]])return kth(ch[x][0],k);
return kth(ch[x][1],k-(siz[ch[x][0]]+1));
}
int pre(int x){int k=Rank(x)-1;return kth(rt,k);};
int suf(int x){int k=Rank(x+1);return kth(rt,k);};

然后这题就做完了

Code:

#include<bits/stdc++.h>
#include<time.h>
#include<ctime>
const int N=1.2e6+5;
using namespace std;
int n,m,cnt,rt;
int siz[N],val[N],pri[N];
int ch[N][2];
struct pi{
int a,b;
pi(int a_=0,int b_=0)
{
a=a_,b=b_;
}
};
void upd(int x){siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;};
pi split(int x,int k)
{
if(!x)return pi(0,0);
if(val[x]<k)//完全在右子树
{
pi y=split(ch[x][1],k);
ch[x][1]=y.a;//将分裂后的左子树与x合并(k不在y.a中)
upd(x);
return pi(x,y.b);//最后分裂出了 x y.b 返回这两棵树
}
else//存在于左子树内
{
pi y= split(ch[x][0],k);
ch[x][0]=y.b;
upd(x);
return pi(y.a,x);
}
}
int merge(int x,int y)
{
if(!x||!y)return x+y;
if(pri[x]<pri[y])//有点左偏树的味道
{
ch[x][1]=merge(ch[x][1],y);
upd(x);
return x;
}
else
{
ch[y][0]=merge(x,ch[y][0]);//始终满足y子树内任意值比x大
upd(y);
return y;
}
}
void insert(int k)
{
val[++cnt]=k;pri[cnt]=rand()*5+rand();siz[cnt]=1;
pi x=split(rt,k);
rt=merge(merge(x.a,cnt),x.b);
}
void del(int k)
{
pi x,y;
x=split(rt,k);//x.a:[0,k-1] x.b:[k,inf]
y=split(x.b,k+1);//y.a[k,k] y.b:[k+1,inf]
y.a=merge(ch[y.a][0],ch[y.a][1]);
x.b=merge(y.a,y.b);
rt=merge(x.a,x.b);
}
int Rank(int k)
{
int res=0;
pi x=split(rt,k);
res=siz[x.a]+1;
merge(x.a,x.b);
return res;
}
int kth(int x,int k)
{
if(k==siz[ch[x][0]]+1)return val[x];
if(k<=siz[ch[x][0]])return kth(ch[x][0],k);
return kth(ch[x][1],k-(siz[ch[x][0]]+1));
}
int pre(int x){int k=Rank(x)-1;return kth(rt,k);};
int suf(int x){int k=Rank(x+1);return kth(rt,k);};
int main()
{
//freopen("P6136.in","r",stdin);
cin>>n>>m;
srand(clock());
for(int i=1,x;i<=n;i++)
{
scanf("%d",&x);
insert(x);
}
int ans=0,last=0;
for(int i=1,opt,x;i<=m;i++)
{
scanf("%d%d",&opt,&x);
x^=last;
switch(opt)
{
case 1: insert(x);break;
case 2: del(x);break;
case 3: last=Rank(x);ans^=last;break;
case 4: last=kth(rt,x);ans^=last;break;
case 5: last=pre(x);ans^=last;break;
case 6: last=suf(x);ans^=last;break;
}
}
printf("%d",ans);
}
posted @   liuboom  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示