推荐资源
- Splay简易教程 By Tiger0132
- Splay入门解析【保证让你看不懂(滑稽)】 By 小蒟蒻yyb
- 史上最详尽的平衡树(splay)讲解与模板 By Clove_unique
- 史上第二详尽的平衡树(Splay)详解 By A_Comme_Amour
- STL乱搞 By qwerta
模版
题目传送门:luogu3369
题目
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
插入xx数
删除xx数(若有多个相同的数,因只删除一个)
查询xx数的排名(排名定义为比当前数小的数的个数+1+1。若有多个相同的数,因输出最小的排名)
查询排名为xx的数
求xx的前驱(前驱定义为小于xx,且最大的数)
求xx的后继(后继定义为大于xx,且最小的数)
输入输出格式
输入格式:
第一行为nn,表示操作的个数,下面nn行每行有两个数optopt和xx,optopt表示操作的序号( 1 \leq opt \leq 6 1≤opt≤6 )
输出格式:
对于操作3,4,5,63,4,5,6每行输出一个数,表示对应答案
输入输出样例
输入样例#1:
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
输出样例#1:
106465
84185
492737
分析
分析见代码
代码
/********************
User:Mandy
Language:c++
Problem:luogu3369
algorithm:Splay
********************/
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int n,tot,root=0;
int size[maxn],cnt[maxn],child[maxn][2],father[maxn],val[maxn];
template<typename T>inline void read(T &x)
{
x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9') {f|=(c=='-');c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
if(f)x=-x;
}
template<typename T>void putch(const T x)
{
if(x>9) putch(x/10);
putchar((x%10)|48);
}
template<typename T>inline void put(const T x)
{
if(x<0) putchar('-'),putch(-x);
else putch(x);
}
void docu()
{
freopen("1.txt","r",stdin);
}
bool get_child(int now)
{
return val[now]>val[father[now]];
//法二:return now==child[father[now]][1];
}
void pushup(int now)
{
size[now]=size[child[now][0]]+size[child[now][1]]+cnt[now];
//不要打错了
}
void rotate(int now)
{
int fa=father[now],an=father[fa],pos=get_child(now),son=child[now][pos^1];
//更新父子关系,注意顺序
//可能会造成father[0]有值,不过不影响
child[fa][pos]=son;father[son]=fa;
child[an][get_child(fa)]=now;father[now]=an;
child[now][pos^1]=fa;father[fa]=now;
//维护size数组
pushup(fa);pushup(now);//注意顺序
}
void Splay(int now,int aim=0)//x要成为目标的儿子
{
while(father[now]!=aim)
{
int fa=father[now],an=father[fa];
if(an!=aim)//当祖父也不是目标
{
if(get_child(now)==get_child(fa)) rotate(fa);
else rotate(now);
//祖父,父亲,儿子在同一直线上,则先翻转父亲,,可以优化树的形态
}
rotate(now);//可不加else
}
if(!aim) root=now;//更新根节点
}
void insert(int x)
{
int now=root,f=0;
//当找到空位置或已有位置
while(now&&val[now]!=x){f=now; now=child[now][x>val[now]];}
if(now) ++cnt[now];//若这个值已有编号
else
{
//获得新编号
now=++tot;//维护now,splay要用
//对父亲的操作
//if(!f) root=tot;多余的
if(f) child[f][x>val[f]]=tot;
//if(f) ++size[f];Splay后会加重
//对新点的操作
father[tot]=f;val[tot]=x;
size[tot]=cnt[tot]=1;
child[tot][0]=child[tot][1]=0;
}
Splay(now);//不能写成tot;
//维持平衡 维护节点信息
}
void find(int x)
//如果x在树上,那么可以返回准确位置,
//如果x不在树上,则返回与它相邻的数(可能大于,也可能小于)
//可以在主程序中加个特判
{
int now=root;
while(val[now]!=x&&child[now][x>val[now]]) now=child[now][x>val[now]];
Splay(now);//旋到根节点
}
int get_kth(int k)//因为插了一个极大,一个极小,所以主程序中记得加1
{
int now=root;
while(1)//直到找到后才退出
{
if(k<=size[child[now][0]]) now=child[now][0];//k不大于左子树节点数
else if(k>size[child[now][0]]+cnt[now])//k大于左子树与根节点
{
k-=size[child[now][0]]+cnt[now];//size别打掉了
now=child[now][1];
}
else return now;
}
}
int get_pre(int x)
{
find(x);
if(val[root]<x) return root;
int now=child[root][0];
while(child[now][1]) now=child[now][1];
return now;
}
int get_suc(int x)
{
find(x);
if(val[root]>x) return root;
int now=child[root][1];
while(child[now][0]) now=child[now][0];
return now;
}
void erase(int x)
{
int pre=get_pre(x);
int suc=get_suc(x);
Splay(pre);Splay(suc,pre);
int now=child[suc][0];
--cnt[now];//别忘了减cnt
//size[now]--是不需要的,Splay会更新,重新插入也会更新,求kth时只会访问到 0(或者下面的splay维护过)
if(cnt[now]) {Splay(now);}//还有剩就旋上去 (维护节点信息)
else
{
child[suc][0]=0;
/* father[now]=0;
pushup(suc);
pushup(pre);
//不需要,理由同size, 若有剩余,上面的splay已经维护了
*/
}
}
void work()
{
insert(inf);
insert(-inf);//插入好排名
for(int i=1;i<=n;++i)
{
int opt,x;
read(opt);read(x);
switch(opt){
case 1:{insert(x);break;}
case 2:{erase(x);break;}
case 3:{find(x);put(size[child[root][0]]);putchar('\n');break;}
case 4:{put(val[get_kth(x+1)]);putchar('\n');break;}
case 5:{put(val[get_pre(x)]);putchar('\n');break;}
default:{put(val[get_suc(x)]);putchar('\n');break;}
}
}
}
int main()
{
// docu();
read(n);
work();
return 0;
}
区间反转
void pushdown(int x) {
if (rev[x]) {
swap(ch[x][0], ch[x][1]);
rev[ch[x][0]] ^= 1;
rev[ch[x][1]] ^= 1;
rev[x] = 0;
}
}
int kth(int k) {
int cur = root;
while (1) {
pushdown(cur);
if (ch[cur][0] && k <= size[ch[cur][0]]) {
cur = ch[cur][0];
} else if (k > size[ch[cur][0]] + cnt[cur]) {
k -= size[ch[cur][0]] + cnt[cur];
cur = ch[cur][1];
} else {
return cur;
}
}
}
void reverse(int l, int r) {
int x = kth(l), y = kth(r+2);
splay(x); splay(y, x);
rev[ch[y][0]] ^= 1;
}
void output(int x) {
pushdown(x);
if (ch[x][0]) output(ch[x][0]);
if (val[x] && val[x] <= n) printf("%d ", val[x]);
if (ch[x][1]) output(ch[x][1]);
}
vector
#include<bits/stdc++.h>
using namespace std;
vector<int>a;
int opt,x,n;
int main(){
scanf("%d",&n);
while(n -- ){
scanf("%d%d",&opt,&x);
switch(opt){
case 1:{a.insert(lower_bound(a.begin(),a.end(),x),x);break;}
case 2:{a.erase(lower_bound(a.begin(),a.end(),x));break;}
case 3:{printf("%d\n",lower_bound(a.begin(),a.end(),x) - a.begin() + 1);break;}
case 4:{printf("%d\n",a[x - 1]);break;}
case 5:{printf("%d\n",*--lower_bound(a.begin(),a.end(),x));break;}
default:{printf("%d\n",*upper_bound(a.begin(),a.end(),x));break;}
}
}
return 0;
}
multiset (60)
#include<bits/stdc++.h>
using namespace std;
int opt,x,n;
multiset<int>s;
int main(){
scanf("%d",&n);
while(n --){
scanf("%d%d",&opt,&x);
switch(opt){
case 1:{s.insert(x);break;}
case 2:{s.erase(s.lower_bound(x));break;}
case 3:{printf("%d\n",distance(s.begin(),s.lower_bound(x)) + 1);break;}
case 4:{multiset<int>::iterator it = s.begin();advance(it,x - 1);printf("%d\n",(*it));break;}
case 5:{printf("%d\n",*--s.lower_bound(x));break;}
default:{printf("%d\n",*s.upper_bound(x));break;}
}
}
return 0;
}