华师一附中OI组
标题:
P3369 【模板】普通平衡树
[打印本页]
作者:
admin
时间:
2020-3-22 18:54
标题:
P3369 【模板】普通平衡树
https://www.luogu.com.cn/problem/P3369
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
插入 x 数
删除 x 数(若有多个相同的数,因只删除一个)
查询 x 数的排名(排名定义为比当前数小的数的个数 +1 )
查询排名为 x 的数
求 x 的前驱(前驱定义为小于 x,且最大的数)
求 x 的后继(后继定义为大于 x,且最小的数)
输入格式
第一行为 n,表示操作的个数,下面 n 行每行有两个数opt 和 xx,opt 表示操作的序号(1≤opt≤6 )
输出格式
对于操作 3,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
说明/提示
【数据范围】
对于 100% 的数据,1≤n≤10^5∣x∣≤10^7。
作者:
admin
时间:
2020-3-22 18:55
训练简单平衡树的好题,可以用多种树结构做,我先做了有个替罪羊树:
#include<bits/stdc++.h>
using namespace std;
const int N = 1001005;
const float p=0.75;
struct node
{
int val,cnt;
int ls,rs,sz; ///lsrs左右儿子 sz本子树下所有节点个数
} a[N];
int ta[N],ita;///转移数组
int n, tot, rt;
void ArrayPr()
{
for (int i=1; i<=tot; i++) cout<<setw(3)<<i;
cout<<endl;
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].val;
cout<<endl;
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].cnt;
cout<<endl;
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].ls;
cout<<endl;
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].rs;
cout<<endl;
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].sz;
cout<<endl;
}
void BSTPr(int rt) ///中序输出以rt为根的BST
{
if (a[rt].ls>0) BSTPr(a[rt].ls);
cout<<a[rt].val<<' '; /// 也可以加上cnt
if (a[rt].rs>0) BSTPr(a[rt].rs);
}
void pushup(int rt)
{
int l=a[rt].ls, r=a[rt].rs;
a[rt].sz=a[l].sz+a[r].sz+a[rt].cnt;
}
int build(int l, int r)//l-r重新建树
{
if(l>r) return 0;
int m=(l+r)/2; ///根结点是最中间的那个
a[ta[m]].ls=build(l,m-1);
a[ta[m]].rs=build(m+1,r);
pushup(ta[m]);
return ta[m];
}
void Inorder(int rt)//倒到数组
{
if(rt>0)
{
Inorder(a[rt].ls);
ta[++ita]=rt;
Inorder(a[rt].rs);
}
}
void RB(int &rt) ///重建rt
{
if(rt==0) return;
float t=a[rt].sz*p; ///左右儿子不能大于rt的0,75
bool b1=a[a[rt].ls].sz>t;
bool b2=a[a[rt].rs].sz>t;
if( b1|| b2)
{
ita=0;
Inorder(rt);
rt=build(1,ita);
}
}
void Ins(int &rt, int x)
{
if(rt==0)
{
rt=++tot;
a[rt].val=x;
a[rt].sz=a[rt].cnt = 1;
}
else
{
RB(rt); /// 比原版多一句调整
if(a[rt].val==x) a[rt].cnt ++,a[rt].sz++;
else
{
if(a[rt].val<x) Ins(a[rt].rs, x);
else if(a[rt].val>x) Ins(a[rt].ls, x);
pushup(rt); /// 比原版多一句调整 线段树相似
}
}
}
int DelMin(int &rt) ///原版不变
{
int t;
if(a[rt].ls==0)///没有左儿子 那就是它
{
t=rt;
rt=a[rt].rs;
return t;
}
else
{
t=DelMin(a[rt].ls); ///递归
pushup(rt);///多了这个调整
return t;
}
}
void Del(int &rt,int x)
{
if (rt==0) return ;
else if(a[rt].val>x) ///左儿子中去找
{
Del(a[rt].ls,x);
pushup(rt);
}
else if(a[rt].val<x) ///右儿子中去找
{
Del(a[rt].rs,x);
pushup(rt);
}
else if(a[rt].val==x)
{
if(a[rt].cnt>1)a[rt].cnt--,a[rt].sz--; ///次数>1
else if(a[rt].ls==0)rt=a[rt].rs;
else if(a[rt].rs==0)rt=a[rt].ls;
else
{
int t=DelMin(a[rt].rs);
a[rt].val=a[t].val;
a[rt].cnt=a[t].cnt;
pushup(rt);
}
}
}
int Rank(int rt, int x) ///在RT子树中比x小的数 +1
{
if(a[rt].val==x) return a[a[rt].ls].sz+1; /// 找到了
if(a[rt].val<x) return a[a[rt].ls].sz +a[rt].cnt+ Rank(a[rt].rs,x);///左子树下面的都算
if(a[rt].val>x) return Rank(a[rt].ls,x);
}
int Getkth(int rt, int x) ///在RT子树中找第k小
{
int l=a[a[rt].ls].sz; /// RT的左儿子下的个数
if(l+1<=x && x<=l+a[rt].cnt) return a[rt].val;
if(l+1>x) return Getkth(a[rt].ls, x); ///左儿子里去找
if(l+a[rt].cnt<x) return Getkth(a[rt].rs, x-(l+a[rt].cnt));
}
int Pre(int rt, int x)
{
int p=rt, ans;
while(p>0)
{
if(x<=a[p].val) p=a[p].ls;
else
{
ans=p;
p=a[p].rs;
}
}
return ans;
}
int Suc(int rt, int x)
{
int p=rt, ans;
while(p>0)
{
if(a[p].val<=x) p=a[p].rs;
else
{
ans = p;
p=a[p].ls;
}
}
return ans;
}
int main()
{
cin>>n;
while(n--)
{
int opt, x;
cin>>opt>>x;
if(opt == 1) Ins(rt, x);
if(opt == 2) Del(rt, x);
if(opt == 3) cout<<Rank(rt, x)<<endl;
if(opt == 4) cout<<Getkth(rt, x)<<endl;
if(opt == 5) cout<<a[Pre(rt, x)].val<<endl;
if(opt == 6) cout<<a[Suc(rt, x)].val<<endl;
///ArrayPr();
///BSTPr(rt);
}
return 0;
}
复制代码
作者:
admin
时间:
2020-3-29 10:37
再来一个 treap
#include<bits/stdc++.h>
using namespace std;
const int inf=9999999;
const int N=100100;
struct node
{
int val,cnt;
int ls,rs,sz,rd; ///lsrs左右儿子 sz本子树下所有节点个数 rd随机值
} a[N];
int n, tot, rt;
void ArrayPr() /// 调试监视使用
{
cout<<endl<<" i ";
for (int i=1; i<=tot; i++) cout<<setw(3)<<i;
cout<<endl<<"val ";
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].val;
cout<<endl<<"cnt ";
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].cnt;
cout<<endl<<" ls ";
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].ls;
cout<<endl<<" rs ";
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].rs;
cout<<endl<<" sz ";
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].sz;
cout<<endl<<" rd ";
for (int i=1; i<=tot; i++) cout<<setw(3)<<a[i].rd;
cout<<endl;
}
void BSTPr(int rt) ///中序输出以rt为根的BST
{
if (a[rt].ls>0) BSTPr(a[rt].ls);
cout<<a[rt].val<<' '; /// 也可以加上cnt
if (a[rt].rs>0) BSTPr(a[rt].rs);
}
void pushup(int rt) ///类似线段树网上调整
{
int l=a[rt].ls, r=a[rt].rs;
a[rt].sz=a[l].sz+a[r].sz+a[rt].cnt;
}
void RR(int &rt) /// 把x的左儿子旋上来当根
{
int l=a[rt].ls;
a[rt].ls=a[l].rs;///左儿子的右儿子变成了我的左儿子
a[l].rs=rt;///自己变成了左儿子的右儿子
pushup(rt), pushup(l),rt=l;///挂上
}
void LR(int &rt)/// 把x的右儿子旋上来当根
{
int r=a[rt].rs;
a[rt].rs=a[r].ls;///右儿子的左儿子变成了我的右儿子
a[r].ls=rt;///自己变成了右儿子的左儿子
pushup(rt),pushup(r),rt=r;///挂上
}
void Ins(int &rt, int x) /// 在rt字数中插入x
{
if(rt==0) ///空树就新建
{
rt=++tot;
a[rt].val=x;
a[rt].sz=a[rt].cnt=1;
a[rt].rd=rand();
}
else
{
a[rt].sz++;
if(a[rt].val==x) a[rt].cnt++;
else
{
if(a[rt].val<x)
{
///cout<<'R';
Ins(a[rt].rs,x);
if(a[rt].rd>a[a[rt].rs].rd) LR(rt);
}
else if(a[rt].val>x)
{
///cout<<'L';
Ins(a[rt].ls, x);
if(a[rt].rd>a[a[rt].ls].rd) RR(rt);
}
}
}
}
void Del(int &rt,int x)
{
if(rt>0)
if(a[rt].val==x)
{
if(a[rt].cnt>1) a[rt].cnt--, a[rt].sz--; //有相同的就直接cnt--
else
{
if (a[rt].ls==0) rt=a[rt].rs;
else if (a[rt].rs==0) rt=a[rt].ls;
else
{
if (a[a[rt].ls].rd>a[a[rt].rs].rd) RR(rt);
else LR(rt);
Del(rt,x);
}
}
}
else
{
a[rt].sz--;
if (a[rt].val<x) Del(a[rt].rs, x);
else Del(a[rt].ls, x);
}
}
int Rank(int rt, int x) ///在RT子树中比x小的数 +1
{
if(a[rt].val==x) return a[a[rt].ls].sz+1; /// 找到了
if(a[rt].val<x) return a[a[rt].ls].sz +a[rt].cnt+ Rank(a[rt].rs,x);///左子树下面的都算
if(a[rt].val>x) return Rank(a[rt].ls,x);
}
int Getkth(int rt, int k) ///在RT子树中找第k小
{
int l=a[a[rt].ls].sz; /// RT的左儿子下的个数
if(l+1<=k && k<=l+a[rt].cnt) return a[rt].val;
if(l+1>k) return Getkth(a[rt].ls, k); ///左儿子里去找
if(l+a[rt].cnt<k) return Getkth(a[rt].rs, k-(l+a[rt].cnt));///右儿子里去找
}
int Pre(int rt, int x) ///小于x的最大数 这里好像有问题
{
if(rt==0) return 0;
if(a[rt].val>=x) return Pre(a[rt].ls,x);//递归查找左子树
else
{
int t=Pre(a[rt].rs,x);
if (a[t].val<a[rt].val) return rt;//找右子树中是否存在前驱
else return t;
}
}
int Suc(int rt, int x) ///大于x的最小
{
int p=rt, ans;
while(p>0)
{
if(a[p].val<=x) p=a[p].rs;
else
{
ans=p;
p=a[p].ls;
}
}
return ans;
}
int main()
{
cin>>n;
while(n--)
{
int opt, x;
cin>>opt>>x;
if(opt==1) Ins(rt, x);
if(opt==2) Del(rt, x);
if(opt==3) cout<<Rank(rt, x)<<endl;
if(opt==4) cout<<Getkth(rt, x)<<endl;
if(opt==5)
{
a[0].val=-inf;
cout<<a[Pre(rt, x)].val<<endl;
}
if(opt==6) cout<<a[Suc(rt, x)].val<<endl;
///ArrayPr();
///BSTPr(rt);
}
return 0;
}
/*
10
1 1
1 2
1 5
1 8
1 9
1 7
1 6
1 1
1 2
1 9
*/
复制代码
欢迎光临 华师一附中OI组 (http://hsyit.cn/)
Powered by Discuz! X3.2