权值线段树

定义

线段树学习

权值线段树和线段树类似,只是维护的数据不一样而已。权值线段树的叶节点维护的是数据出现的次数。

为了防止数据过大,一般先离散化数据再使用权值线段树维护。

主席树的前置技能为权值线段树。

权值线段树可以解决:

                       ①插入x

                       ②删除x数(若有多个相同的数,因只删除一个)

                       ③查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)

                       ④查询排名为x的数

                       ⑤求x的前驱(前驱定义为小于x,且最大的数)

                       ⑥求x的后继(后继定义为大于x,且最小的数)

实现

P3369 【模板】普通平衡树

#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
struct node
{int L,R,w;}tree[maxn<<2];
int op[maxn],w[maxn];
vector<int> scatter;
void build(int L,int R,int k);
int get(int x);
void update(int L,int R,int w,int f,int k);
int rank_of_x(int L,int R,int l,int r,int k);
int rank_is_x(int L,int R,int rank,int k);
int main()
{
  int i,n,N;
  scanf("%d",&n);
  for(i=1;i<=n;i++)
  {
    scanf("%d%d",&op[i],&w[i]);
    if(op[i]!=4) scatter.push_back(w[i]);
  }
  sort(scatter.begin(),scatter.end());
  scatter.resize(unique(scatter.begin(),scatter.end())-scatter.begin());
  N=scatter.size();build(1,N,1);
  for(i=1;i<=n;i++)
  {
    if(op[i]==1) update(1,N,get(w[i]),1,1);
    else if(op[i]==2) update(1,N,get(w[i]),-1,1);
    else if(op[i]==3) printf("%d\n",rank_of_x(1,N,1,get(w[i])-1,1)+1);
    else if(op[i]==4) printf("%d\n",scatter[rank_is_x(1,N,w[i],1)-1]);
    else if(op[i]==5) printf("%d\n",scatter[rank_is_x(1,N,rank_of_x(1,N,1,get(w[i])-1,1),1)-1]);
    else printf("%d\n",scatter[rank_is_x(1,N,rank_of_x(1,N,1,get(w[i]),1)+1,1)-1]);
  }
  system("pause");
  return 0;
}
void build(int L,int R,int k)
{
  tree[k].L=L;tree[k].R=R;
  if(L==R)
  {
    tree[k].w=0;
    return ;
  }
  int mid=(L+R)/2;
  build(L,mid,k<<1);
  build(mid+1,R,k<<1|1);
  tree[k].w=tree[k<<1].w+tree[k<<1|1].w;
}
int get(int x)
{return lower_bound(scatter.begin(),scatter.end(),x)-scatter.begin()+1;}
void update(int L,int R,int w,int f,int k)
{
  if(L==R)
  {
    tree[k].w+=f;
    return ;
  }
  int mid=(L+R)/2;
  if(w<=mid) update(L,mid,w,f,k<<1);
  else update(mid+1,R,w,f,k<<1|1);
  tree[k].w=tree[k<<1].w+tree[k<<1|1].w;
}
int rank_of_x(int L,int R,int l,int r,int k)
{
  if(l>r) return 0;
  if(L>=l&&R<=r) return tree[k].w;
  int mid=(L+R)/2,ans=0;
  if(l<=mid) ans+=rank_of_x(L,mid,l,r,k<<1);
  if(r>mid) ans+=rank_of_x(mid+1,R,l,r,k<<1|1);
  return ans;
}
int rank_is_x(int L,int R,int rank,int k)
{
  if(L==R) return R;
  int mid=(L+R)/2;
  if(rank<=tree[k<<1].w) return rank_is_x(L,mid,rank,k<<1);
  else return rank_is_x(mid+1,R,rank-tree[k<<1].w,k<<1|1);
}

转载于:https://www.cnblogs.com/VividBinGo/p/11483841.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值