题面链接
打完平衡树模板后第一道实战题
思路:平衡树套平衡树
最开始
铅笔在纸上游走,一点思路也没有……
后来回归原始,想着用数组(编号桶排序)+前缀和(位置),结果发现对于100%的数据,所有出现的书的编码为不大于2147483647的正数。
发现要炸,那么有没有一种方法,使得编号储存和查找不费空间,又不费时间;使位置储存和查找不费空间,又不费时间呢?
想到刚打完模板题的平衡树:由于平衡树具有有序性,而且可以保持树的期望树高为 O ( log n ) O(\log n) O(logn),每次操作期望时间复杂度为 Θ ( log n ) \Theta(\log n) Θ(logn)
我们可以利用此特性提高我们查书的速度
构造
既然查编号要查得快,查位置也要查得快,不如就给这两者都设一棵平衡树:
对于每一个特定编号,都设一棵平衡树记录这一编号的书所在的位置,
再设一棵平衡树将所有特定编号串起来以便查询。
查询
那么对于查询操作,先在编号平衡树里找到编号,再在这个编号对应的位置平衡树里
找到范围内最左边的书(左边界的后继)和最右边(右边界的前驱)的书
获取这两本书在位置平衡树里的排名,二者相减+1就能得到这一区间内书的数量
时间复杂度
l
o
g
n
×
4
l
o
g
n
logn\times 4logn
logn×4logn,省略常数后得
Θ
(
log
n
2
)
Θ(\log n^2)
Θ(logn2)
修改
对于修改操作,在编号平衡树里找到被取代的书编号,再在这个编号对应的位置平衡树里删除书本,如果本编号的书删没了,顺便把编号也删了
再把插入的书插入到平衡树里,时间复杂度 2 × log n × log n 2\times \log n \times \log n 2×logn×logn即 Θ ( log n 2 ) Θ(\log n^2) Θ(logn2)
一次AC哦!
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<ctime>
#include<assert.h>
using namespace std;
int m,n,x,y,que,lt,rt,bo,ans,tmp[600005];
char ch;
struct posnode{//记录某一编码的所有书的位置的平衡树
posnode *son[2];
int pos;int key;
int tsize;
void getsize()//获取树的大小
{
this->tsize=1;
if(this->son[0]) this->tsize+=this->son[0]->tsize;
if(this->son[1]) this->tsize+=this->son[1]->tsize;
}
}mem1[6666666],*now1 = mem1;
struct node{
node *son[2];//son[0]:left son;son[1]:right son;
int val;int key;
posnode *rec;//记录某一编码的所有书的位置的平衡树
}mem[6666666],*now = mem,*root;
posnode *newposi(int where)
{
posnode *q = now1++;
q->pos = where;
q->key = rand();
q->son[0] = q->son[1] = NULL;
q->tsize = 1;
return q;
}
node *newnode(int num)
{
node *p = now++;
p->val = num;
p->key = rand();
p->rec = NULL;
return p;
}
void Rotatep(posnode *&p,int k)//k=0:左转 k=1:右转
{
posnode *tmp = p->son[k^1];
p->son[k^1] = tmp->son[k];
tmp->son[k] = p;
p->getsize();//记得更新!!!!
p = tmp;
p->getsize();//记得更新!!!
return ;
}
void Rotate(node *&p,int k)//k=0:左转 k=1:右转
{
node *tmp = p->son[(k^1)];
p->son[(k^1)] = tmp->son[k];
tmp->son[k] = p;
p = tmp;
return ;
}
void Insertrec(posnode *&q,int where)//维护记录本编码的书的位置的平衡树
{
// cout<<"ok2"<<endl;
if(q == NULL)//找到位置就放
{
// cout<<"ok2"<<endl;
q = newposi(where);
return ;
}
// cout<<"what's up"<<endl;
int k = where > q->pos;//书的位置小于当前取0,大于当前取1
Insertrec(q->son[k] , where);
q->getsize();//更新
if(q->key > q->son[k]->key)//破坏堆序就旋转
Rotatep(q,(k^1));
return ;
}
void Insertx(node *&p,int num,int where)//插入新书
{
// cout<<"ok"<<endl;
if(p == NULL)//找到位置就放
{
p = newnode(num);
// cout<<"ok"<<endl;
Insertrec(p->rec,where);//记录
return ;
}
if(p->val == num)//编码相同就记录
{
Insertrec(p->rec,where);
return ;
}
int k = num > p->val;//编码小于当前取0,大于当前取1
Insertx(p->son[k] , num, where);
if(p->key > p->son[k]->key)//破坏堆序就旋转
Rotate(p,(k^1));
return ;
}
void Deleterec(posnode *&q,int where)
{
if(q == NULL) return ;//空即返回
if(q->pos == where)//找到书的位置
{
if(q->son[1]&&q->son[0])//有两个儿子
{
int k = q->son[0]->key < q->son[1]->key;//如果左儿子修正值>右儿子修正值就右旋,否则左旋
Rotatep(q ,k);
Deleterec(q->son[k],where);
q->getsize();//更新树的大小
}
else
{
if(q->son[1])//只有右儿子
q = q->son[1];
else
{
if(q->son[0])//只有左儿子
q = q->son[0];
else//没有儿子直接删除
q = NULL;
}
}
return ;
}
else
{
int k = where > q->pos;
Deleterec(q->son[k],where);//继续找书的位置
q->getsize();//更新书的数量
}
}
void Deletex(node *&p,int num,int where)
{
if(p == NULL) return ;//空即返回
if(p->val == num)
{
Deleterec(p->rec,where);//将书本 9从书架上取下;
if(p->rec)
return ;
else
{
if(p->son[1]&&p->son[0])//有两个儿子
{
int k = p->son[0]->key < p->son[1]->key;//如果左儿子修正值>右儿子修正值就右旋,否则左旋
Rotate(p ,(k^1));
Deletex(p->son[(k^1)],num,where);
}
else
{
if(p->son[1])
p = p->son[1];
else
{
if(p->son[0])
p = p->son[0];
else
p = NULL;
}
}
return ;
}
}
else
{
int k = num > p->val;
Deletex(p->son[k],num, where);//继续找书的编号
}
}
int Getsuc(posnode *&q,int num)//求后继
{
if(q == NULL) return -1;//没有找到返回-1
if(num > q->pos)//找比num大的数
return Getsuc(q->son[1],num);
else
{ //最小的比num大的数
int tmp = Getsuc(q->son[0],num);
if(tmp != -1) return tmp;
else return q->pos;
}
}
int Getpre(posnode *&q,int num)//求前驱
{
if(q == NULL) return -1;
if(num < q->pos)//找比num小的数
return Getpre(q->son[0],num);
else
{ //最大比num小的数
int tmp = Getpre(q->son[1],num);
if(tmp != -1) return tmp;
else return q->pos;
}
}
int Getrank(posnode *&q,int num)//求排名
{
if (q == NULL) return 0;
if (q->pos >= num) return Getrank(q->son[0], num);//
int lsize = 0;
if( q->son[0] ) lsize = q->son[0]->tsize;
return lsize + Getrank(q->son[1], num) + 1;
}
void Getans(node *&p,int num,int l,int r)//查询操作
{
if(p == NULL) return ;
if(num == p->val)
{
ans = 0;
int rightbook = Getpre(p->rec,r);//找到范围内最右边的书
if(rightbook == -1) return ;
int leftbook = Getsuc(p->rec,l); //找到范围内最左边的书
if(leftbook == -1) return ;
int lrank = Getrank(p->rec,leftbook);//求出左边的书在本编码所有书内的排名
int rrank = Getrank(p->rec,rightbook);//求出右边的书在本编码所有书内的排名
ans = rrank - lrank + 1;//范围内书的数量
return ;
}
else
{
int k = num > p->val;
Getans(p->son[k],num,l,r);
return ;
}
}
int main()
{
root = NULL;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++)//插入原始书的序列
{
scanf("%d", &tmp[i]);
Insertx(root,tmp[i],i);
}
for(int i = 1;i <= m;i ++)
{
cin>>ch;
if(ch == 'Q')//查询操作
{
scanf("%d%d%d",<,&rt,&bo);
ans = 0;
Getans(root,bo,lt,rt);
printf("%d\n",ans);
}
else
{
scanf("%d%d",&x,&y);//修改=插入+删除
Deletex(root,tmp[x],x);
Insertx(root,y,x);
tmp[x]=y;
}
}
return 0;
}