COGS 普通平衡树
【题目描述】
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
【输入格式】
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
【输出格式】
对于操作3,4,5,6每行输出一个数,表示对应答案
【样例输入】
10 1 106465 4 1 1 317721 1 460929 1 644985 1 84185 1 89851 6 81968 1 492737 5 493598
【样例输出】
106465 84185 492737
【提示】
题解:对于数据范围,我们可以现将输入的数据进行离散化,对于每一个输入的数据建立线段树的节点,在每一个节点内保存区间和,代表着比他大的树有多少个,对于插入和删除一个数,我们可以利用insert+1或-1就好了,查询排名的时候,我们只需要查询1到x-1的个数+1,查询X的排名的时候,可以分别查找左右子树,对于5,6操作,我们可以在区间1到x-1或x+1到N中查找,在查找的过程中例如查找前趋,我们可以再查找的时候加一个判断,看看在这个子树中能不能找到,如果不能找到,再返回另一个子树中的值,但是这样做会导致时间效率很低,导致开始我T了5个点,所以这个时候我们可以在线段树的每个节点中多保存一个maxn(前驱)和minn(后继),这样在查找的时候就可以大大缩短时间效率。
在放代码之前我们先看一下离散化怎样去写:
因为输入的数只有100000个,岁我们可以把这100000个数先排序,排完序以后再去重,然后我们再将每个数中的排名加入到数据中就好了,最后再加上一个数的映射。
int main()
{
scanf("%d",&n);
for(i=1;i<=n;++i)
{
scanf("%d%d",&a[i].kind,&a[i].xx);
if(a[i].kind!=4)
{
tot+=1;
c[tot]=a[i].xx;
}
}
sort(c+1,c+tot+1);
size=unique(c+1,c+tot+1)-c-1; // unique()是一个去重的函数,函数返回值是去掉重复元素后最后一位在数组中的位置,所以我们在使用的时候应该讲数据先进行排序,值得一提的是,再去重的过程中并没有把重复的元素删去,而是保留在了数组的最后.
// size 可以看做是长度
for(i=1;i<=n;++i)
{
if(a[i].kind!=4)
{
tot=a[i].xx;
a[i].xx=upper_bound(c+1,c+size+1,a[i].xx)-c-1; //将a[]中的元素换成排名
d[a[i].xx]=tot; //d[] 为一个映射。
}
}
}
下面放上代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define L k<<1,l,mid
#define R k<<1|1,mid+1,r
#include<algorithm>
using namespace std;
struct point
{
int kind,xx;
}a[100001];
struct S
{
int sum,maxn,minn;
}tr[400000];
int n,c[100001]={0},d[100001]={0},size;
inline void updata(int k)
{
tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
if (tr[k<<1|1].maxn==0) tr[k].maxn=tr[k<<1].maxn;
else tr[k].maxn=tr[k<<1|1].maxn;
if (tr[k<<1].minn==0) tr[k].minn=tr[k<<1|1].minn;
else tr[k].minn=tr[k<<1].minn;
}
inline void insert(int k,int l,int r,int x,int y)
{
int mid=(l+r)>>1;
if(l==r)
{
tr[k].sum+=y;
if(tr[k].sum!=0) tr[k].maxn=tr[k].minn=l;
else tr[k].maxn=tr[k].minn=0;
return ;
}
if(x<=mid) insert(L,x,y);
else insert(R,x,y);
updata(k);
}
inline int NO(int k,int l,int r,int x,int y)
{
int mid=(l+r)>>1,num=0;
if(x<=l&&y>=r) return tr[k].sum;
if(x<=mid) num=NO(L,x,y);
if(y>mid) num+=NO(R,x,y);
return num;
}
inline int work(int k,int l,int r,int x)
{
int mid=(l+r)>>1,num,sum;
if(l==r) return l;
if(x<=tr[k<<1].sum) return work(L,x);
else return work(R,x-tr[k<<1].sum);
}
struct S work1(int k,int l,int r,int x,int y)
{
int mid=(l+r)>>1;
struct S ans,ans1,ans2;
ans1.maxn=ans1.minn=ans2.maxn=ans2.minn=0;
if(x<=l&&y>=r) return tr[k];
if(x<=mid) ans1=work1(L,x,y);
if(y>mid) ans2=work1(R,x,y);
if(ans1.minn) ans.minn=ans1.minn;
else ans.minn=ans2.minn;
if(ans2.maxn) ans.maxn=ans2.maxn;
else ans.maxn=ans1.maxn;
return ans;
}
int main()
{
freopen("phs.in","r",stdin);
freopen("phs.out","w",stdout);
int i,j,t,x,y,tot=0;
struct S ans;
scanf("%d",&n);
for(i=1;i<=n;++i)
{
scanf("%d%d",&a[i].kind,&a[i].xx);
if(a[i].kind!=4)
{
tot+=1;
c[tot]=a[i].xx;
}
}
sort(c+1,c+tot+1);
size=unique(c+1,c+tot+1)-c-1;
for(i=1;i<=n;++i)
{
if(a[i].kind!=4)
{
tot=a[i].xx;
a[i].xx=upper_bound(c+1,c+size+1,a[i].xx)-c-1;
d[a[i].xx]=tot;
}
}
for(i=1;i<=n;++i)
{
if(a[i].kind==1) insert(1,1,size,a[i].xx,1);
if(a[i].kind==2) insert(1,1,size,a[i].xx,-1);
if(a[i].kind==3)
{
if(a[i].xx==1) printf("1\n");
else printf("%d\n",NO(1,1,size,1,a[i].xx-1)+1);
}
if(a[i].kind==4) printf("%d\n",d[work(1,1,size,a[i].xx)]);
if(a[i].kind==5) printf("%d\n",d[work1(1,1,size,1,a[i].xx-1).maxn]);
if(a[i].kind==6) printf("%d\n",d[work1(1,1,size,a[i].xx+1,size).minn]);
}
}

1819

被折叠的 条评论
为什么被折叠?



