作用
线段树的作用是区间修改和查询,平衡树的作用是查询第k大,k的排名,前驱,后继。这两个结合起来,就变成了可以区间修改和查询第k大,k的排名,前驱,后继的数据结构:树套树-线段树套平衡树。
实现
先构造出线段树,每个线段树除了记录左边界和右边界之外,还储存了一棵平衡树(当然实际上只需要储存根节点),对应着这一个区间的所有数。每次修改区间L~R时,需要将所有包含L~R的线段树节点的平衡树都修正。操作其实没有什么难点,和普通线段树一样分块处理即可,效率为 O(log22(n)) 。
但是查询区间第k大不能像普通线段树一样,必须用二分枚举答案mid,然后查询mid的排名,如果排名<=k就增大mid,否则减小mid。假设二分跨度为t,则效率为
O(log22(n)∗log2(t))
。
ps:树套树常数较大,请谨慎使用。
模板
以BZOJ3196为例,这里使用的是线段树套Treap。注意细节处理。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=50000,maxt=1700000,MAXINT=((1<<30)-1)*2+1;
int n,te,a[maxn+5];
//============================================================
struct Node
{
Node *son[2];
int val,fix,si,w;
int cmp(int k) {if (k==val) return -1;if (k<val) return 0; else return 1;}
void Pushup() {si=son[0]->si+w+son[1]->si;}
};
typedef Node* P_node;
Node tem[maxt+5];
P_node null=tem,len=null;
P_node newNode(int k)
{
len++;len->son[0]=len->son[1]=null;
len->si=len->w=1;len->val=k;len->fix=rand();
return len;
}
void Rotate(P_node &p,int d)
{
P_node t=p->son[d^1];p->son[d^1]=t->son[d];t->son[d]=p;
p->Pushup();t->Pushup();p=t;
}
void Insert(P_node &p,int k)
{
if (p==null) {p=newNode(k);return;}
int d=p->cmp(k);
if (d==-1) p->w++; else
{
Insert(p->son[d],k);
if (p->son[d]->fix>p->fix) Rotate(p,d^1);
}
p->Pushup();
}
void Delete(P_node &p,int k)
{
if (p==null) return;
int d=p->cmp(k);
if (d==-1)
{
if (p->w>1) p->w--; else
if (p->son[0]==null) p=p->son[1]; else
if (p->son[1]==null) p=p->son[0]; else
{
int d;if (p->son[0]->fix>p->son[1]->fix) d=0; else d=1;
Rotate(p,d);if (p==null) return;Delete(p->son[d],k);
}
if (p==null) return;
} else Delete(p->son[d],k);
p->Pushup();
}
int getrank(P_node p,int k) //对于不存在的k,排名是k后继的排名
{
if (p==null) return 1;
int d=p->cmp(k);
if (d==-1) return p->son[0]->si+1; else
if (d==0) return getrank(p->son[0],k); else
return getrank(p->son[1],k)+p->son[0]->si+p->w;
}
int getpre(P_node p,int k)
{
if (p==null) return -MAXINT;
int d=p->cmp(k);
if (d==1) return max(getpre(p->son[1],k),p->val); else
return getpre(p->son[0],k);
}
int getsuf(P_node p,int k)
{
if (p==null) return MAXINT;
int d=p->cmp(k);
if (d==0) return min(getsuf(p->son[0],k),p->val); else
return getsuf(p->son[1],k);
} //以上为Treap
//============================================================
struct SegmentTree
{
int l[4*maxn+5],r[4*maxn+5];P_node ro[4*maxn+5]; //只保留根指针
void Build(int p,int L,int R)
{
l[p]=L;r[p]=R;ro[p]=null;
if (L==R) return;
int mid=L+(R-L>>1);
Build(p<<1,L,mid);Build(p<<1|1,mid+1,R);
}
void Seg_Insert(int p,int L,int k)
{
if (L<l[p]||r[p]<L) return;
if (l[p]<=L&&L<=r[p]) Insert(ro[p],k);
//这里不是L<=l[p]&&r[p]<=L,因为所有包含L的节点都要插入k
if (l[p]==r[p]) return;
Seg_Insert(p<<1,L,k);Seg_Insert(p<<1|1,L,k);
}
void Seg_Delete(int p,int L,int k)
{
if (L<l[p]||r[p]<L) return;
if (l[p]<=L&&L<=r[p]) Delete(ro[p],k);
//同理
if (l[p]==r[p]) return;
Seg_Delete(p<<1,L,k);Seg_Delete(p<<1|1,L,k);
}
int Seg_rank(int p,int L,int R,int k) //这个函数返回真正的答案-1,防止重复
{
if (R<l[p]||r[p]<L) return 0;
if (L<=l[p]&&r[p]<=R) return getrank(ro[p],k)-1;
//这里是普通线段树查询,所以是L<=l[p]&&r[p]<=R
return Seg_rank(p<<1,L,R,k)+Seg_rank(p<<1|1,L,R,k);
}
int Seg_kth(int l,int r,int k)
{
int L=0,R=1e8;
while (L<=R) //二分
{
int mid=L+(R-L>>1),rk=Seg_rank(1,l,r,mid)+1;
if (rk<=k) L=mid+1; else R=mid-1;
}
return R;
}
int Seg_pre(int p,int L,int R,int k)
{
if (R<l[p]||r[p]<L) return -MAXINT;
if (L<=l[p]&&r[p]<=R) return getpre(ro[p],k);
return max(Seg_pre(p<<1,L,R,k),Seg_pre(p<<1|1,L,R,k));
}
int Seg_suf(int p,int L,int R,int k)
{
if (R<l[p]||r[p]<L) return MAXINT;
if (L<=l[p]&&r[p]<=R) return getsuf(ro[p],k);
return min(Seg_suf(p<<1,L,R,k),Seg_suf(p<<1|1,L,R,k));
}
};
SegmentTree tr; //以上为线段树
//============================================================
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x)
{
int tot=0,f=1;char ch=getchar(),lst=' ';
while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}
if (lst=='-') f=-f;
while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
x=tot*f;
return Eoln(ch);
}
void LNR(P_node ro)
{
if (ro==null) return;
LNR(ro->son[0]);for (int i=1;i<=ro->w;i++) printf("%d\n",ro->val);LNR(ro->son[1]);
}
int main()
{
freopen("STBST.in","r",stdin);
freopen("STBST.out","w",stdout);
readi(n);readi(te);tr.Build(1,1,n);
for (int i=1;i<=n;i++) readi(a[i]),tr.Seg_Insert(1,i,a[i]);
while (te--)
{
int td,x,y,z;readi(td);readi(x);readi(y);
switch (td)
{
case 1:readi(z);printf("%d\n",tr.Seg_rank(1,x,y,z)+1);break;
case 2:readi(z);printf("%d\n",tr.Seg_kth(x,y,z));break;
case 3:tr.Seg_Delete(1,x,a[x]);tr.Seg_Insert(1,x,y);a[x]=y;break;
case 4:readi(z);printf("%d\n",tr.Seg_pre(1,x,y,z));break;
case 5:readi(z);printf("%d\n",tr.Seg_suf(1,x,y,z));break;
}
}
return 0;
}