权值线段树
对权值作为维护对象而开的线段树,即每个点存的是区间内对应数字的某种值(出现的次数)。
权值线段树用于维护一个数在一个序列中出现的次数
比如数值1,1,2,2,2,3,4,5,6,7,8
初始每个节点为0
插入1
插入2
插入3
插入4
插入5
插入6
插入7
插入8
模板普通平衡树p3369
题目描述
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1.插入 x 数
2.删除 x 数(若有多个相同的数,因只删除一个)
3.查询 x 数的排名(排名定义为比当前数小的数的个数 +1 )
4.查询排名为 x 的数
5.求 x 的前驱(前驱定义为小于 x,且最大的数)
6.求 x 的后继(后继定义为大于 x,且最小的数)
输入格式
第一行为 n,表示操作的个数,下面 n 行每行有两个数opt 和x,opt 表示操作的序号(1≤opt≤6 )
输出格式
对于操作 3,4,5,6 每行输出一个数,表示对应答案
输入输出样例
输入 #1复制
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
输出 #1复制
106465
84185
492737
说明/提示
【数据范围】
对于 100% 的数据, 1≤n≤105,|x|≤107
void pushup(int rt) { t[rt].v=t[rt<<1].v+t[rt<<1|1].v } void change(int rt,int l,int r,int q,int v) { if (l==r) { t[rt].v+=v;return ; } int mid=(l+r)>>1; if(q<=mid) change(rt<<1,l,mid,q,v); else change(rt<<1|1,mid+1,r,q,v); pushup(rt); } int query_rnk(int rt,int l,int r,int ql,int qr) { if (ql<=l&&r<=qr) return t[rt].v; int mid=(l+r)>>1,ans=0; if(ql<mid) ans+=query_rnk(rt<<1,l,mid,ql,qr); if(qr>mid)ans+=query_rnk(rt<<1|1,mid+1,r,ql,qr); return ans; } int query_num(int rt,int l,int r,int q) { if(l==r) return l; int mid=(l+r)>>1; if(t[rt<<1].v>=q) return query_num(rt<<1,l,mid,q); else return query_num(rt<<1|1,mid+1,r,q-t[rt<<1].v } int main() { for (int i=1;i<=n;i++) { node[i].opt=read(),node[i].val=read(); if (node[i].opt==4) continue; lsh[++tot]=node[i].val; } sort(lsh+1,lsh+tot+1); tot=unique(lsh+1,lsh+tot+1)-lsh-1; for(int i=1;i<=n;i++){ if (node[i].opt!=4) node[i].val=lower_bound(lsh+1,lsh+tot+1,node[i].val)-lsh if(node[i].opt==1) change(1,1,tot,node[i].val,1); if(node[i].opt==2) change(1,1,tot,node[i].val,-1); if(node[i].opt==3){ if (node[i].val==1){ put(“1”);continue; } printf(“%d\n”,query_rnk(1,1,tot,1,node[i].val-1)+1); } if (node[i].opt==4){ printf(“%d\n”,lsh[query_num(1,1,tot,node[i].val)]);} if(node[i].opt==5){ int rk=query_rnk(1,1,tot,1,node[i].val-1); printf(“%d\n”,lsh[query_num(1,1,tot,rk)]); } if (node[i].opt==6){ int rk=query_rnk(1,1,tot,1,node[i].val)+1; printf(“%d\n”,lsh[query_num(1,1,tot,rk)]); } }
其中change()是单点修改,query_rnk()正常区间求和,而query_num()是权值线段树特有的操作,也就是查询第k大值,不过权值线段树的空间优势仅在离线或值域较小的情况有优势。
权值树重要性质
对于一个线段树而言,如果它的总长度不变,那么它的形态不会变,也就是在len不变的情况下,权值树形态不会变,这样来我们可以对权值进行加减操作。