一、题目
二、解法
所谓树套树呢?就是把两棵不同的树套在一起,以达到意想不到的效果。
对于这道题,可以线段树套平衡树,也就是每个点维护一个平衡树,下面我来分别讲解一下每种操作。
- 查询k在区间内的排名
把区间分解成线段树上的点,查询 k k k在每个点上的排名-1然后累加,最后在加上1。 - 查询区间内排名为k的值
先二分一个值,然后用拿到这个值的最大排名(我没有把相同的点合并为一个点),然后找到满最大排名大于等于 k k k的最小的 m i d mid mid,就是答案。 - 修改某一位值上的数值
暴力在线段树上单点修改,删去原来的数字后加入新给的数字。 - 查询k在区间内的前驱
先分解区间,然后取每个点找到的前驱中最大的。 - 查询k在区间内的后继
与上面的操作方式类似。
总时间复杂度
O
(
n
log
3
n
)
O(n\log^3 n)
O(nlog3n),卡常让你自闭 。
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <cstdio>
#include <iostream>
using namespace std;
#define inf 2147483647
const int MAXN = 50005;
const int MAXM = 30*MAXN;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,cnt,a[MAXN],seed=4088;
int ch[MAXM][2],siz[MAXM],val[MAXM],hp[MAXM];
struct node
{
int p[2];
node() {p[0]=p[1]=0;}
}emp;
struct tree
{
int l,r,rt;
}tr[MAXM];
int rand(){return seed=seed*33457+52331314;}
void up(int x)
{
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
}
node split(int x,int v)
{
if(!x) return emp;
int d=val[x]<=v;
node y=split(ch[x][d],v);
ch[x][d]=y.p[d^1];
y.p[d^1]=x;
up(x);
return y;
}
node split_(int x,int s)
{
if(!x) return emp;
node y;
if(siz[ch[x][0]]>=s)
{
y=split_(ch[x][0],s);
ch[x][0]=y.p[1];
y.p[1]=x;
}
else
{
y=split_(ch[x][1],s-siz[ch[x][0]]-1);
ch[x][1]=y.p[0];
y.p[0]=x;
}
up(x);
return y;
}
int merge(int x,int y)
{
if(!x || !y) return x+y;
if(hp[x]<hp[y])
{
ch[x][1]=merge(ch[x][1],y);
up(x);
return x;
}
ch[y][0]=merge(x,ch[y][0]);
up(y);
return y;
}
void ins(int &rt,int v)
{
node x=split(rt,v-1);
int t=++cnt;
siz[t]=1;val[t]=v;hp[t]=rand();
rt=merge(x.p[0],merge(t,x.p[1]));
}
void del(int &rt,int v)
{
node x=split(rt,v-1),y=split_(x.p[1],1);
if(val[y.p[0]]!=v)
rt=merge(x.p[0],merge(y.p[0],y.p[1]));
else
rt=merge(x.p[0],y.p[1]);
}
int rank_min(int rt,int v)
{
node x=split(rt,v-1);
int ans=siz[x.p[0]]+1;
rt=merge(x.p[0],x.p[1]);
return ans;
}
int rank_max(int rt,int v)
{
node x=split(rt,v);
int ans=siz[x.p[0]];
rt=merge(x.p[0],x.p[1]);
return ans;
}
int pre(int x,int v)
{
if(!x) return -inf;
if(val[x]>=v)
return pre(ch[x][0],v);
return max(val[x],pre(ch[x][1],v));
}
int suf(int x,int v)
{
if(!x) return inf;
if(val[x]<=v)
return suf(ch[x][1],v);
return min(val[x],suf(ch[x][0],v));
}
void build(int i,int l,int r)
{
tr[i].l=l;tr[i].r=r;
for(int p=l;p<=r;p++)
{
ins(tr[i].rt,a[p]);
}
if(l==r)
return ;
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
}
void modify(int i,int id,int x)
{
del(tr[i].rt,a[id]);
ins(tr[i].rt,x);
if(tr[i].l==tr[i].r) return ;
int mid=(tr[i].l+tr[i].r)>>1;
if(id<=mid)
modify(i<<1,id,x);
else
modify(i<<1|1,id,x);
}
int Rank_Min(int i,int l,int r,int v)
{
if(l>tr[i].r || r<tr[i].l)
return 0;
if(l<=tr[i].l && tr[i].r<=r)
return rank_min(tr[i].rt,v)-1;
return Rank_Min(i<<1,l,r,v)+Rank_Min(i<<1|1,l,r,v);
}
int Rank_Max(int i,int l,int r,int v)
{
if(l>tr[i].r || r<tr[i].l)
return 0;
if(l<=tr[i].l && tr[i].r<=r)
return rank_max(tr[i].rt,v);
return Rank_Max(i<<1,l,r,v)+Rank_Max(i<<1|1,l,r,v);
}
int Pre(int i,int l,int r,int v)
{
if(l>tr[i].r || r<tr[i].l)
return -inf;
if(l<=tr[i].l && tr[i].r<=r)
return pre(tr[i].rt,v);
return max(Pre(i<<1,l,r,v),Pre(i<<1|1,l,r,v));
}
int Suf(int i,int l,int r,int v)
{
if(l>tr[i].r || r<tr[i].l)
return inf;
if(l<=tr[i].l && tr[i].r<=r)
return suf(tr[i].rt,v);
return min(Suf(i<<1,l,r,v),Suf(i<<1|1,l,r,v));
}
int ask(int L,int R,int v)
{
int l=0,r=1e8,ans=0;
while(l<=r)
{
int mid=(l+r)>>1,t=Rank_Max(1,L,R,mid);
if(t>=v)
{
ans=mid;
r=mid-1;
}
else
l=mid+1;
}
return ans;
}
void print(int x)
{
if(!x) return ;
print(ch[x][0]);
printf("%d ",val[x]);
print(ch[x][1]);
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
a[i]=read();
build(1,1,n);
while(m--)
{
int op=read(),l=read(),r=read();
if(op==1)
printf("%d\n",Rank_Min(1,l,r,read())+1);
if(op==2)
printf("%d\n",ask(l,r,read()));
if(op==3)
{
modify(1,l,r);
a[l]=r;
}
if(op==4)
printf("%d\n",Pre(1,l,r,read()));
if(op==5)
printf("%d\n",Suf(1,l,r,read()));
}
}