【洛谷P3380】【模板】二逼平衡树(树套树)
题目
题目链接:https://www.luogu.com.cn/problem/P3380
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
- 查询 \(k\) 在区间内的排名。
- 查询区间内排名为 \(k\) 的值。
- 修改某一位值上的数值。
- 查询 \(k\) 在区间内的前驱(前驱定义为严格小于 \(x\),且最大的数,若不存在输出 \(-2147483647\))。
- 查询 \(k\) 在区间内的后继(后继定义为严格大于 \(x\),且最小的数,若不存在输出 \(2147483647\))。
思路
终于有一次写平衡树调试时间不超过 1h 了 /kk。
一般的二逼平衡树复杂度是 \(O(m\log^3 n)\) 的,瓶颈在于操作 2 需要二分。
但是我们知道有一些二分是可以直接放在线段树上的,这样可以去掉一个 \(\log n\)。但是由于操作二并不是一个前缀的询问,所以没有办法放到线段树上。
但是我们发现,排名为 \(k\) 是一个前缀且支持二分,所以我们考虑换一种思路,我们用线段树维护 val,平衡树维护 key。
也就是线段树上区间 \([l,r]\) 的平衡树表示的是数值在 \([l,r]\) 的位置的下标有哪些。显然一个下标只会被 \(O(\log n)\) 个区间覆盖。所以空间复杂度是 \(O(n\log n)\)。
接下来对于每一个操作:
- 直接查询数值在 \([1,k]\) 中有多少个下标在 \([l,r]\)。
- 在线段树上二分数值,每次查询左子树有多少个下标在 \([l,r]\) 内,选择往左还是往右。
- 将所有包含这个下标的数值区间所对应平衡树内删掉这个下标,然后插入新的下标。为了省空间,我写了一个垃圾回收。
- 先查询 \(k-1\) 在区间内的排名 \(rk\),然后查询排名为 \(rk\) 的数值。
- 先查询 \(k\) 在区间内的排名 \(rk\),然后查询排名为 \(rk+1\) 的数值。
注意询问之前要把所有包含数值的操作离散化。
时空复杂度均为 \(O(m\log^2 n)\)。
代码
4.5Kb。比我想象的长一些。
// I LOVE DATA STRUCTURE!!!
#include <bits/stdc++.h>
using namespace std;
const int N=100010,LG=18,Inf=2147483647;
int n,m,cnt,orz,a[N],b[N];
struct Query
{
int l,r,opt,k;
}ask[N];
struct Treap
{
int lc[N*LG],rc[N*LG],val[N*LG],size[N*LG],dat[N*LG];
queue<int> q;
Treap()
{
while (q.size()) q.pop();
for (int i=1;i<N*LG;i++) q.push(i);
}
int New(int v)
{
int x=q.front(); q.pop();
lc[x]=rc[x]=0; size[x]=1;
val[x]=v; dat[x]=rand();
return x;
}
void pushup(int x)
{
size[x]=size[lc[x]]+size[rc[x]]+1;
}
void zig(int &x)
{
int y=lc[x],z=rc[y];
rc[y]=x; lc[x]=z; x=y;
pushup(rc[x]); pushup(x);
}
void zag(int &x)
{
int y=rc[x],z=lc[y];
lc[y]=x; rc[x]=z; x=y;
pushup(lc[x]); pushup(x);
}
int build()
{
int x=New(Inf),y=New(-Inf);
lc[x]=y; size[x]=2;
return x;
}
int ins(int x,int v)
{
if (!x) x=New(v);
else if (val[x]>v)
{
lc[x]=ins(lc[x],v);
if (dat[lc[x]]>dat[x]) zig(x);
}
else
{
rc[x]=ins(rc[x],v);
if (dat[rc[x]]>dat[x]) zag(x);
}
pushup(x);
return x;
}
int del(int x,int v)
{
if (val[x]==v)
{
if (!lc[x] && !rc[x]) { q.push(x); return 0; }
if (!rc[x] || (lc[x] && dat[rc[x]]<dat[lc[x]]))
zig(x),rc[x]=del(rc[x],v);
else
zag(x),lc[x]=del(lc[x],v);
}
else if (val[x]>v)
{
lc[x]=del(lc[x],v);
if (dat[lc[x]]>dat[x]) zig(x);
}
else
{
rc[x]=del(rc[x],v);
if (dat[rc[x]]>dat[x]) zag(x);
}
pushup(x);
return x;
}
int getrk(int x,int v)
{
if (!x) return 0;
if (val[x]==v) return size[lc[x]];
if (val[x]>v) return getrk(lc[x],v);
if (val[x]<v) return size[lc[x]]+1+getrk(rc[x],v);
return 23333;
}
void debug(int x)
{
if (lc[x]) printf("%d %d\n",val[x],val[lc[x]]);
if (rc[x]) printf("%d %d\n",val[x],val[rc[x]]);
if (lc[x]) debug(lc[x]);
if (rc[x]) debug(rc[x]);
}
}treap;
struct SegTree
{
int rt[N*4];
void build(int x,int l,int r)
{
rt[x]=treap.build();
if (l==r) return;
int mid=(l+r)>>1;
build(x*2,l,mid); build(x*2+1,mid+1,r);
}
void update(int x,int l,int r,int k,int v,bool tag)
{
if (tag) rt[x]=treap.ins(rt[x],v);
else rt[x]=treap.del(rt[x],v);
if (l==r) return;
int mid=(l+r)>>1;
if (k<=mid) update(x*2,l,mid,k,v,tag);
else update(x*2+1,mid+1,r,k,v,tag);
}
int query1(int x,int l,int r,int ql,int qr,int pl,int pr)
{
if (ql<=l && r<=qr)
return treap.getrk(rt[x],pr+1)-treap.getrk(rt[x],pl);
int mid=(l+r)>>1;
int sum=0;
if (ql<=mid) sum+=query1(x*2,l,mid,ql,qr,pl,pr);
if (qr>mid) sum+=query1(x*2+1,mid+1,r,ql,qr,pl,pr);
return sum;
}
int query2(int x,int l,int r,int ql,int qr,int k)
{
if (l==r) return l;
int mid=(l+r)>>1;
int cnt=treap.getrk(rt[x*2],qr+1)-treap.getrk(rt[x*2],ql);
if (k<=cnt) return query2(x*2,l,mid,ql,qr,k);
else return query2(x*2+1,mid+1,r,ql,qr,k-cnt);
}
}seg;
int main()
{
srand(53962);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[++cnt]=a[i];
}
for (int i=1;i<=m;i++)
{
scanf("%d",&ask[i].opt);
if (ask[i].opt==3)
{
scanf("%d%d",&ask[i].l,&ask[i].k);
b[++cnt]=ask[i].k;
}
else
scanf("%d%d%d",&ask[i].l,&ask[i].r,&ask[i].k);
}
sort(b+1,b+1+cnt);
cnt=unique(b+1,b+1+cnt)-b-1;
seg.build(1,1,cnt);
for (int i=1;i<=n;i++)
{
a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;
seg.update(1,1,cnt,a[i],i,1);
}
for (int i=1;i<=m;i++)
{
if (ask[i].opt==1)
{
int k=upper_bound(b+1,b+1+cnt,ask[i].k-1)-b-1;
printf("%d\n",seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r)+1);
}
if (ask[i].opt==2)
printf("%d\n",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,ask[i].k)]);
if (ask[i].opt==3)
{
int k=lower_bound(b+1,b+1+cnt,ask[i].k)-b;
seg.update(1,1,cnt,a[ask[i].l],ask[i].l,0);
seg.update(1,1,cnt,k,ask[i].l,1);
a[ask[i].l]=k;
}
if (ask[i].opt==4)
{
int k=upper_bound(b+1,b+1+cnt,ask[i].k-1)-b-1;
if (k==0) { printf("%d\n",-Inf); continue; }
int rk=seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r);
if (rk==0) { printf("%d\n",-Inf); continue; }
printf("%d\n",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,rk)]);
}
if (ask[i].opt==5)
{
int k=upper_bound(b+1,b+1+cnt,ask[i].k)-b-1;
int rk=seg.query1(1,1,cnt,1,k,ask[i].l,ask[i].r)+1;
if (rk>ask[i].r-ask[i].l+1) { printf("%d\n",Inf); continue; }
printf("%d\n",b[seg.query2(1,1,cnt,ask[i].l,ask[i].r,rk)]);
}
}
return 0;
}