最普通的$O(nlog^3 n)$做法,普通的线段树套无旋树堆。然后无旋树堆似乎常数比较大,T了两个点总之没Splay大,懒得卡了,反正现在也没有会出这种二逼题的出题人
简单说一说树套树,因为可能第一次做不太好理解这个概念,其实说起来比较简单:以我写的这个线段树套平衡树为例,因为题目要我们维护区间,而维护的这些东西恰好是最适合平衡树的。但是平衡树有限,只维护序列就维护不了数,只维护数的集合就维护不了序列,于是想办法解决:发现线段树每个节点是一段区间,然后我们就可以把每个节点代表的区间塞进一棵平衡树里,修改/查询就先在线段树上走到对应的区间(其实走过的一路都是要改的),然后把那个区间里的那棵平衡树上修改/查询。这样空间是$O(nlog n)$级别的,考虑到修改(删除+插入)还要开二倍
具体操作:
1.查询某个数的区间排名
在线段树上找到各个对应区间,然后把每个区间里小于这个数的数的数量加起来,再加一就是排名,复杂度$O(log^2 n)$
2.查询区间内某个排名的数
这个做法下没有什么好办法,只能二分这个数,复杂度$O(log^3 n)$。这里还要注意边界问题,如果你查的是排名(这个数第一次出现的位置)要在小于等于的时候取答案,如果你查的是这个数最后一次出现的位置要在大于等于的时候取答案,看个人习惯
3.单点修改
在线段树上一路走下去,在每个区间先删掉原来那个数再把新的数插进去,记得最后把原数组也改掉,复杂度$O(log^2 n)$
4.查询区间内某个数的前驱
在线段树上找到各个对应区间,在每个区间里查前驱,最后取最大值,复杂度$O(log^2 n)$
5.查询区间内某个数的后继
类似4.的,在线段树上找到各个对应区间,在每个区间里查后继,最后取最小值,复杂度$O(log^2 n)$
1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=50005,K=32,inf=2147483647,Maxn=1e8+8; 7 int a[N],root[4*N],son[N*K][2]; 8 int siz[N*K],val[N*K],rnk[N*K]; 9 int n,m,x,y,z,t1,t2,t3,t4,cnt,tot; 10 int Newnode(int tsk)//无旋树堆开始 11 { 12 siz[++tot]=1; 13 val[tot]=tsk; 14 rnk[tot]=rand(); 15 return tot; 16 } 17 void Pushup(int nde) 18 { 19 siz[nde]=siz[son[nde][0]]+siz[son[nde][1]]+1; 20 } 21 int Merge(int x,int y) 22 { 23 if(!x||!y) return x+y; 24 else 25 { 26 if(rnk[x]<=rnk[y]) 27 { 28 son[x][1]=Merge(son[x][1],y); 29 Pushup(x); return x; 30 } 31 else 32 { 33 son[y][0]=Merge(x,son[y][0]); 34 Pushup(y); return y; 35 } 36 } 37 } 38 void Split(int nde,int &x,int &y,int tsk) 39 { 40 if(!nde) x=y=0; 41 else 42 { 43 if(val[nde]<=tsk) 44 x=nde,Split(son[nde][1],son[nde][1],y,tsk); 45 else 46 y=nde,Split(son[nde][0],x,son[nde][0],tsk); 47 Pushup(nde); 48 } 49 } 50 void Insert(int &t,int tsk) 51 { 52 Split(t,x,y,tsk); 53 t=Merge(Merge(x,Newnode(tsk)),y); 54 } 55 void Delete(int &t,int tsk) 56 { 57 Split(t,x,z,tsk),Split(x,x,y,tsk-1); 58 y=Merge(son[y][0],son[y][1]),t=Merge(Merge(x,y),z); 59 } 60 int Find(int nde,int tsk)//无旋树堆结束 61 { 62 if(tsk<=siz[son[nde][0]]) return Find(son[nde][0],tsk); 63 else if(tsk==siz[son[nde][0]]+1) return nde; 64 else return Find(son[nde][1],tsk-siz[son[nde][0]]-1); 65 } 66 void Build(int t,int l,int r)//建立这个区间的平衡树(其实就是一个个塞进去=。=) 67 { 68 for(int i=l;i<=r;i++) 69 Insert(root[t],a[i]); 70 } 71 void Create(int nde,int l,int r)//线段树开始 72 { 73 Build(nde,l,r); 74 if(l==r) return ; 75 int mid=(l+r)/2,ls=2*nde,rs=2*nde+1; 76 Create(ls,l,mid),Create(rs,mid+1,r); 77 } 78 int Ask(int &t,int tsk,int typ) 79 { 80 if(typ==1) 81 { 82 Split(t,x,y,tsk-1); 83 int ret=siz[x]; 84 t=Merge(x,y); return ret; 85 } 86 else if(typ==2) 87 { 88 Split(t,x,y,tsk-1); 89 if(!siz[x]) return -inf; 90 int ret=val[Find(x,siz[x])]; 91 t=Merge(x,y); return ret; 92 } 93 else if(typ==3) 94 { 95 Split(t,x,y,tsk); 96 if(!siz[y]) return inf; 97 int ret=val[Find(y,1)]; 98 t=Merge(x,y); return ret; 99 } 100 } 101 int Exceed(int typ) 102 { 103 if(typ==1) return 0; 104 else if(typ==2) return -inf; 105 else if(typ==3) return inf; 106 } 107 int Query(int nde,int l,int r,int nl,int nr,int tsk,int typ) 108 { 109 if(l>nr||r<nl) 110 return Exceed(typ); 111 else if(l>=nl&&r<=nr) 112 return Ask(root[nde],tsk,typ); 113 else 114 { 115 int mid=(l+r)/2,ls=2*nde,rs=2*nde+1; 116 int ret1=Query(ls,l,mid,nl,nr,tsk,typ); 117 int ret2=Query(rs,mid+1,r,nl,nr,tsk,typ); 118 if(typ==1) return ret1+ret2; 119 else if(typ==2) return max(ret1,ret2); 120 else if(typ==3) return min(ret1,ret2); 121 } 122 } 123 void Change(int nde,int l,int r,int pos,int tsk)//线段树结束 124 { 125 Delete(root[nde],a[pos]),Insert(root[nde],tsk); 126 if(l==r) return ; 127 int mid=(l+r)/2,ls=2*nde,rs=2*nde+1; 128 if(pos<=mid) Change(ls,l,mid,pos,tsk); 129 else Change(rs,mid+1,r,pos,tsk); 130 } 131 int rnk_query(int l,int r,int tsk)//区间查询某个数的排名 132 { 133 return Query(1,1,n,l,r,tsk,1)+1; 134 } 135 int num_query(int l,int r,int tsk)//查询区间内某个数的排名 136 { 137 int ll=-Maxn,rr=Maxn,ret=0; 138 while(ll<=rr) 139 { 140 int mid=(ll+rr)/2; 141 if(rnk_query(l,r,mid)<=tsk) ll=mid+1,ret=mid; 142 else rr=mid-1; 143 } 144 return ret; 145 } 146 void num_change(int pos,int tsk)//单点修改 147 { 148 Change(1,1,n,pos,tsk),a[pos]=tsk; 149 } 150 int pre_query(int l,int r,int tsk)//区间查询某个数的前驱 151 { 152 return Query(1,1,n,l,r,tsk,2); 153 } 154 int nxt_query(int l,int r,int tsk)//区间查询某个数的后继 155 { 156 return Query(1,1,n,l,r,tsk,3); 157 } 158 int main() 159 { 160 register int i; 161 srand(20020513); 162 scanf("%d%d",&n,&m); 163 for(i=1;i<=n;i++) 164 scanf("%d",&a[i]); 165 Create(1,1,n); 166 for(i=1;i<=m;i++) 167 { 168 scanf("%d%d%d",&t1,&t2,&t3); 169 if(t1!=3) scanf("%d",&t4); 170 if(t1==1) printf("%d ",rnk_query(t2,t3,t4)); 171 else if(t1==2) printf("%d ",num_query(t2,t3,t4)); 172 else if(t1==3) num_change(t2,t3); 173 else if(t1==4) printf("%d ",pre_query(t2,t3,t4)); 174 else if(t1==5) printf("%d ",nxt_query(t2,t3,t4)); 175 } 176 return 0; 177 }