解题:洛谷3380 二逼平衡树

题面

最普通的$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 } 
View Code
原文地址:https://www.cnblogs.com/ydnhaha/p/10021590.html