@
树套树
将两个树形的数据结构进行嵌套的数据结构
线段树套平衡树
https://www.luogu.org/problem/P3380
首先在外层建一棵线段树。
线段树的每一个节点都为\([l,r]\)区间的信息
此时在\([l,r]\)这个节点上建一棵平衡树
然后这种树套树支持的操作:
1)查询z值在\([l,r]\)区间的排名
就类似于普通的线段树查询,将\([l,r]\)拆成\(log(n)\)个区间,然后把对于所有的这些区间在treap里面可以找到有多少个比它小的数
2) 查询\([l,r]\)区间上的值
二分然后使用(1)的函数在check
3)修改某个值
对于所有包含这个数的区间做修改即可。
4)\([l,r]\)寻找前驱/后继
也是拆成\(log(n)\)个区间,然后去查询对于所有这些区间的前驱或后继里面最大/小的那个。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define ls1 k<<1
#define rs1 k<<1|1
#define ls2 x[k].s[0]
#define rs2 x[k].s[1]
using namespace std;
const int N=5e4+5,inf=2147483647;
struct X{
int z1,z2,s[2],sz,sl;
void xg(int z){
z1=z,z2=rand();
sz=sl=1;
}
}x[N*40];
int ans,cz[N],n,m,s;
struct Treap{
int rt;
void pu(int k){
x[k].sz=x[ls2].sz+x[k].sl+x[rs2].sz;
}
void xz(int& k,int p){
int t=x[k].s[p];
x[k].s[p]=x[t].s[p^1];
x[t].s[p^1]=k;
x[t].sz=x[k].sz;
pu(k),k=t;
}
void updt(int& k,int z){
if(!k) x[k=++s].xg(z);
else{
++x[k].sz;
if(x[k].z1==z) ++x[k].sl;
else{
int p=z>x[k].z1;
updt(x[k].s[p],z);
if(x[x[k].s[p]].z2<x[k].z2) xz(k,p);
}
}
}
void del(int& k,int z){
if(x[k].z1==z){
if(x[k].sl>1) --x[k].sl;
else if(ls2&&rs2){
int p=x[rs2].z2<x[ls2].z2;
xz(k,p),del(x[k].s[p^1],z);
}
else k=ls2+rs2;
}
else del(x[k].s[x[k].z1<z],z);
pu(k);
}
int qry1(int k,int z){
if(!k) return 0;
if(z==x[k].z1) return x[ls2].sz;
else if(z<x[k].z1) return qry1(ls2,z);
else return x[ls2].sz+x[k].sl+qry1(rs2,z);
}
void qry4(int k,int z){
if(!k) return;
if(z<=x[k].z1) qry4(ls2,z);
else ans=max(ans,x[k].z1),qry4(rs2,z);
}
void qry5(int k,int z){
if(!k) return;
if(z>=x[k].z1) qry5(rs2,z);
else ans=min(ans,x[k].z1),qry5(ls2,z);
}
}tr[N<<2];
void bt(int k,int l,int r){
for(int i=l;i<=r;++i) tr[k].updt(tr[k].rt,cz[i]);
if(l!=r){
int mid=(l+r)>>1;
bt(ls1,l,mid),bt(rs1,mid+1,r);
}
}
void updt(int k,int l,int r,int w,int z){
tr[k].del(tr[k].rt,cz[w]);
tr[k].updt(tr[k].rt,z);
if(l!=r){
int mid=(l+r)>>1;
w<=mid?updt(ls1,l,mid,w,z):updt(rs1,mid+1,r,w,z);
}
}
int qry1(int k,int l,int r,int ql,int qr,int z){
if(ql<=l&&r<=qr) return tr[k].qry1(tr[k].rt,z);
else{
int mid=(l+r)>>1,re=0;
if(ql<=mid) re+=qry1(ls1,l,mid,ql,qr,z);
if(qr>mid) re+=qry1(rs1,mid+1,r,ql,qr,z);
return re;
}
}
int qry2(int ql,int qr,int z){
int l=-1,r=1e8+1;
while(r-1>l){
int mid=(l+r)>>1;
if(qry1(1,1,n,ql,qr,mid)+1<=z) l=mid;
else r=mid;
}
return l;
}
void qry4(int k,int l,int r,int ql,int qr,int z){
if(ql<=l&&r<=qr) tr[k].qry4(tr[k].rt,z);
else{
int mid=(l+r)>>1;
if(ql<=mid) qry4(ls1,l,mid,ql,qr,z);
if(qr>mid) qry4(rs1,mid+1,r,ql,qr,z);
}
}
void qry5(int k,int l,int r,int ql,int qr,int z){
if(ql<=l&&r<=qr) tr[k].qry5(tr[k].rt,z);
else{
int mid=(l+r)>>1;
if(ql<=mid) qry5(ls1,l,mid,ql,qr,z);
if(qr>mid) qry5(rs1,mid+1,r,ql,qr,z);
}
}
int main(){
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
srand(n);
for(int i=1;i<=n;++i) scanf("%d",&cz[i]);
bt(1,1,n);
while(m--){
int opt,l,r,k;
scanf("%d",&opt);
if(opt==3){
scanf("%d%d",&l,&k);
updt(1,1,n,l,k);
cz[l]=k;
}
else{
scanf("%d%d%d",&l,&r,&k);
if(opt==1) ans=qry1(1,1,n,l,r,k)+1;
else if(opt==2) ans=qry2(l,r,k);
else if(opt==4) ans=-inf,qry4(1,1,n,l,r,k);
else ans=inf,qry5(1,1,n,l,r,k);
printf("%d\n",ans);
}
}
return 0;
}
树状数组套线段树
同样的问题,也可以这样的树套树解决。
外层树状数组维护区间,内部线段树维护权值(首先要输入所有值和查询修改操作再离散化)。
具体来说树状数组tr [ i ]这个点上
保存了[ i - lowbit(i) +1 , i ]这个区间的一个值域的线段树
然后每次查询 [ l , r ] 上 [ x , y ]的信息时,
就要先设一个数组,记录 [1 , l-1 ] 和[ 1, r ]根据拆分之后的的几个区间的根节点的位置
1)查询z值在[l,r]区间的排名
我们首先如上文所说挑出那些节点之后,
就查询这些节点对应的线段树里面小于z的值有多少个即可
2)查询排名为x在\([l,r]\)中的数是哪个
每次统计左儿子的权值和,就可以知道这个数是在左区间还是右区间。
然后再不断递归即可
3)修改
这个操作类似与一开始加入节点的操作,其实就是在包含这个数的树状数组中进行-1+1的修改即可
4)找前驱
用1)找到它的排名,然后用2)找到\([l,r]\)找名次为它的名次减1的数就可以了。
5)找后继同理
#include<cstdio>
#include<algorithm>
#define ls x[k].s[0]
#define rs x[k].s[1]
using namespace std;
const int N=5e4+5,inf=2147483647;
struct X{
int s[2],z;
}x[N*100];
int a[N],val[N<<1],tn,n,m;
int rt[N],cnt;
int s1,q1[25],s2,q2[25];
void updt(int& k,int l,int r,int w,int z){
if(!k) k=++cnt;
x[k].z+=z;
if(l!=r)
{
int mid=(l+r)>>1;
w<=mid?updt(ls,l,mid,w,z):updt(rs,mid+1,r,w,z);
}
}
void add(int i,int z){
int w=lower_bound(val+1,val+tn+1,a[i])-val;
for(;i<=n;i+=i&-i) updt(rt[i],1,tn,w,z);
}
void go_son(int p){
for(int i=1;i<=s1;++i) q1[i]=x[q1[i]].s[p];
for(int i=1;i<=s2;++i) q2[i]=x[q2[i]].s[p];
}
int calc(){
int re=0;
for(int i=1;i<=s1;++i) re-=x[x[q1[i]].s[0]].z;
for(int i=1;i<=s2;++i) re+=x[x[q2[i]].s[0]].z;
return re;
}
int qry1(int l,int r,int z){
if(l==r) return 0;
int mid=(l+r)>>1,tz;
if(z<=mid){
go_son(0);
return qry1(l,mid,z);
}
tz=calc();
go_son(1);
return tz+qry1(mid+1,r,z);
}
int qry2(int l,int r,int z){
if(l==r) return val[l];
int mid=(l+r)>>1,tz=calc();
if(z<=tz){
go_son(0);
return qry2(l,mid,z);
}
go_son(1);
return qry2(mid+1,r,z-tz);
}
struct Q{
int opt,l,r,k;
void in(){
scanf("%d%d%d",&opt,&l,&r);
if(opt==3) val[++tn]=r;
else{
scanf("%d",&k);
if(opt!=2) val[++tn]=k;
}
}
void init(){
s1=s2=0;
for(int i=l-1;i;i-=i&-i) q1[++s1]=rt[i];
for(int i=r;i;i-=i&-i) q2[++s2]=rt[i];
}
void cl(){
int ans;
if(opt==3){
add(l,-1);
a[l]=r;
add(l,1);
}
else{
init();
if(opt!=2) k=lower_bound(val+1,val+tn+1,k)-val;
if(opt==1) ans=qry1(1,tn,k)+1;
else if(opt==2) ans=qry2(1,tn,k);
else if(opt==4){
int rk=qry1(1,tn,k);
init();
if(!rk) ans=-inf;
else ans=qry2(1,tn,rk);
}
else{
int rk=qry1(1,tn,k+1);
init();
if(rk>r-l) ans=inf;
else ans=qry2(1,tn,rk+1);
}
printf("%d\n",ans);
}
}
}q[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
val[++tn]=a[i];
}
for(int i=1;i<=m;++i) q[i].in();
sort(val+1,val+tn+1);
tn=unique(val+1,val+tn+1)-val-1;
for(int i=1;i<=n;++i) add(i,1);
for(int i=1;i<=m;++i) q[i].cl();
return 0;
}