bzoj3196: Tyvj 1730 二逼平衡树
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #define maxn 50005 7 #define maxk 2000005 8 #define inf 100000000 9 using namespace std; 10 11 int n,m,tot,sum,Ans,root[maxn<<2],val[maxk],times[maxk],size[maxk],fa[maxk],son[maxk][2],a[maxn]; 12 struct Tsegment{ 13 void clear(){ 14 tot=0,memset(val,0,sizeof(val)); 15 memset(root,0,sizeof(0)); 16 memset(son,0,sizeof(son)); 17 memset(fa,0,sizeof(fa)); 18 memset(times,0,sizeof(times)); 19 memset(size,0,sizeof(size)); 20 } 21 int which(int x){return son[fa[x]][1]==x;} 22 void update(int x){ 23 size[x]=size[son[x][0]]+size[son[x][1]]+times[x]; 24 } 25 void rotata(int x){ 26 int y=fa[x],d=which(x),dd=which(y); 27 if (fa[y]) son[fa[y]][dd]=x; fa[x]=fa[y]; 28 fa[son[x][d^1]]=y,son[y][d]=son[x][d^1]; 29 fa[y]=x,son[x][d^1]=y,update(y); 30 } 31 void splay(int x,int goal,int op){ 32 while (fa[x]!=goal){ 33 if (fa[fa[x]]==goal) rotata(x); 34 else if (which(x)==which(fa[x])) rotata(fa[x]),rotata(x); 35 else rotata(x),rotata(x); 36 } 37 update(x); if (goal==0) root[op]=x; 38 } 39 void insert(int k,int x){ 40 int y=root[k]; bool bo; 41 if (y==0){ 42 root[k]=++tot,val[tot]=x,times[tot]=size[tot]=1,fa[tot]=son[tot][0]=son[tot][1]=0; 43 return; 44 } 45 for (;;){ 46 bo=0; 47 if (val[y]==x) times[y]++,size[y]++,bo=1,splay(y,0,k); 48 else if (x<val[y]){ 49 if (!son[y][0]) son[y][0]=++tot,val[tot]=x,times[tot]=size[tot]=1,fa[tot]=y,bo=1,splay(tot,0,k); 50 else y=son[y][0]; 51 }else{ 52 if (!son[y][1]) son[y][1]=++tot,val[tot]=x,times[tot]=size[tot]=1,fa[tot]=y,bo=1,splay(tot,0,k); 53 else y=son[y][1]; 54 } 55 if (bo==1) break; 56 } 57 } 58 void query(int k,int x){ 59 int y=root[k]; if (y==0) return; 60 for (;;){ 61 if (y==0) break; 62 if (val[y]==x){ 63 sum+=size[son[y][0]]; 64 break; 65 }else if (val[y]<x){ 66 sum+=(size[son[y][0]]+times[y]); 67 y=son[y][1]; 68 }else y=son[y][0]; 69 } 70 } 71 int prep(int k,int x){ 72 splay(x,0,k); 73 int y=son[x][0]; 74 while (son[y][1]) y=son[y][1]; 75 return y; 76 } 77 void DLT(int k,int x){ 78 int y=prep(k,x),z; 79 if (y==0){ 80 splay(x,0,k); 81 z=son[x][1]; 82 fa[z]=0,son[x][0]=son[x][1]=0,fa[x]=0,root[k]=z,times[x]=size[x]=0; 83 splay(z,0,k); 84 }else{ 85 splay(y,0,k),splay(x,y,k); 86 z=son[x][1]; 87 fa[z]=y,son[y][1]=z,fa[x]=son[x][0]=son[x][1]=size[x]=times[x]=0; 88 update(y); 89 } 90 } 91 void dlt(int k,int x){ 92 int y=root[k]; if (y==0) return; bool bo; 93 for (;;){ 94 bo=0; 95 if (val[y]==x){ 96 if (times[y]>1) times[y]--,size[y]--,bo=1,splay(y,0,k); 97 else{ 98 times[y]--,size[y]--,bo=1,DLT(k,y); 99 } 100 }else if (x<val[y]) y=son[y][0]; 101 else y=son[y][1]; 102 if (bo==1) break; 103 } 104 } 105 void query4(int k,int x){ 106 int y=root[k]; if (y==0) return; 107 for (;;){ 108 if (y==0) break; 109 if (val[y]<x) Ans=max(Ans,val[y]),y=son[y][1]; 110 else y=son[y][0]; 111 } 112 } 113 void query5(int k,int x){ 114 int y=root[k]; if (y==0) return; 115 for (;;){ 116 if (y==0) break; 117 if (x<val[y]) Ans=min(Ans,val[y]),y=son[y][0]; 118 else y=son[y][1]; 119 } 120 } 121 }Splay; 122 struct date{ 123 void insert(int k,int l,int r,int x){ 124 Splay.insert(k,a[x]); 125 if (l==r) return; 126 int mid=(l+r)>>1; 127 if (x<=mid) insert(k*2,l,mid,x); 128 else insert(k*2+1,mid+1,r,x); 129 } 130 void query(int k,int l,int r,int x,int y,int z){ 131 if (l>=x&&r<=y){ 132 Splay.query(k,z); 133 return; 134 }int mid=(l+r)>>1; 135 if (x<=mid) query(k*2,l,mid,x,y,z); 136 if (y>mid) query(k*2+1,mid+1,r,x,y,z); 137 } 138 int check(int x,int y,int z){ 139 sum=0,query(1,1,n,x,y,z); 140 return sum; 141 } 142 void dlt(int k,int l,int r,int x,int y){ 143 Splay.dlt(k,y); 144 if (l==r) return; 145 int mid=(l+r)>>1; 146 if (x<=mid) dlt(k*2,l,mid,x,y); 147 else dlt(k*2+1,mid+1,r,x,y); 148 } 149 void query4(int k,int l,int r,int x,int y,int z){ 150 if (l>=x&&r<=y){ 151 Splay.query4(k,z); 152 return; 153 }int mid=(l+r)>>1; 154 if (x<=mid) query4(k*2,l,mid,x,y,z); 155 if (y>mid) query4(k*2+1,mid+1,r,x,y,z); 156 } 157 void query5(int k,int l,int r,int x,int y,int z){ 158 if (l>=x&&r<=y){ 159 Splay.query5(k,z); 160 return; 161 }int mid=(l+r)>>1; 162 if (x<=mid) query5(k*2,l,mid,x,y,z); 163 if (y>mid) query5(k*2+1,mid+1,r,x,y,z); 164 } 165 }Tree; 166 struct note{ 167 void query1(int x,int y,int z){ 168 sum=0,Tree.query(1,1,n,x,y,z); 169 printf("%d\n",++sum); 170 } 171 void query2(int x,int y,int z){ 172 int low=0,high=inf,mid,ans=0; 173 while (low<=high){ 174 mid=(low+high)/2; 175 if (Tree.check(x,y,mid)+1<=z) ans=mid,low=mid+1; 176 else high=mid-1; 177 } 178 printf("%d\n",ans); 179 } 180 void query3(int x,int y){ 181 Tree.dlt(1,1,n,x,a[x]); 182 a[x]=y,Tree.insert(1,1,n,x); 183 } 184 void query4(int x,int y,int z){ 185 Ans=0; 186 Tree.query4(1,1,n,x,y,z); 187 printf("%d\n",Ans); 188 } 189 void query5(int x,int y,int z){ 190 Ans=inf; 191 Tree.query5(1,1,n,x,y,z); 192 printf("%d\n",Ans); 193 } 194 }Task; 195 196 int main(){ 197 int oppo,u,v,w; 198 scanf("%d%d",&n,&m); 199 Splay.clear(); 200 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 201 for (int i=1;i<=n;i++) Tree.insert(1,1,n,i); 202 while (m--){ 203 scanf("%d%d%d",&oppo,&u,&v); 204 if (oppo==1) scanf("%d",&w),Task.query1(u,v,w); 205 else if (oppo==2) scanf("%d",&w),Task.query2(u,v,w); 206 else if (oppo==3) Task.query3(u,v); 207 else if (oppo==4) scanf("%d",&w),Task.query4(u,v,w); 208 else scanf("%d",&w),Task.query5(u,v,w); 209 } 210 return 0; 211 }
题目链接: http://www.lydsy.com/JudgeOnline/problem.php?id=3196
题目大意:
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)
序列长度为n,操作个数为m,n<=5*10^4,m<=5*10^4;
做法:对于区间最大值或区间最小值我们可以用线段树,对于整个序列第k小,求排名,求前驱,求后继,我们可以用splay,-->那么对于区间求排名,求第k小,求前驱,求后继,我们可以用树套树,线段树套splay,线段树维护区间位置,对于线段树的每个节点,我们维护一棵splay,维护这段区间中的权值。每加入一个点,需要增开logn的空间,故空间复杂度为(n+m)logn,操作1,3,4,5的时间复杂度均为log^2 n,操作2的时间复杂度为log^3 n;可以过此题。
对于操作1:我们在线段树上找相应的区间,在其splay上询问有多少个节点权值<它,最后把sum++,即为答案,脑补;
对于操作2:我们考虑二分答案,即转化为了操作1,若该答案的排名<=k,ans=mid,l=mid+1,否则r=mid-1,输出ans即可;
对于操作3:线段树中,将所有包含该位置的点原来的权值删去,并将新权值加入;
对于操作4:在每棵splay上查找即可(splay具有二分性质);
对于操作5:与操作4相似;
注意:线段树记录相应splay的根,每次将某个节点旋到所在splay树上的根是,记得更新线段树记录的根,以防出错。
具体细节见代码。
树套树,线段树套splay。