权值线段树

对权值作为维护对象而开的线段树,即每个点存的是区间内对应数字的某种值(出现的次数)。

权值线段树用于维护一个数在一个序列中出现的次数

比如数值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不变的情况下,权值树形态不会变,这样来我们可以对权值进行加减操作。

 

posted @ 2022-11-22 09:44  心悟&&星际  阅读(45)  评论(0编辑  收藏  举报