权值线段树就是在线段树的基础上,将每一个点作为一个桶。区间l到r表示从[l…r]这个数值之间的信息。每个节点维护一个数值的数量,表示[l…r]这个区间有多少个数。
支持的操作:
添加一个元素
查找一个元素出现的次数
查找区间的元素个数
查询所有元素的第k大(小)元素
/*
权值线段树
在线段树的基础上,将每一个点作为一个桶。
区间l到r表示从[l...r]这个数值之间的信息。
每个节点维护一个数值的数量,表示[l..r]这个区间有多少个数。
*/
#include <iostream>
using namespace std;
const int maxn = 1e5;
struct node{
int l,r,num;
}a[4*maxn];
void update(int x)
{
a[x].num = a[2*x].num + a[2*x+1].num;
}
void build(int x,int l,int r) //当前点为x,x的左右区间为l,r,递归建树
{
a[x].l = l;
a[x].r = r;
if( l == r )
{
a[x].num = 0;
return;
}
int mid = ( l + r ) / 2;
build(2*x,l,mid);
build(2*x+1,mid+1,r);
update(x);
}
void add(int x,int t,int v) //当前节点为x,添加v个t
{
if( a[x].l == a[x].r ) //单点修改
{
a[x].num += v;
return;
}
int mid = ( a[x].l + a[x].r ) / 2;
if( t <= mid ) add(2*x,t,v);
else add(2*x+1,t,v);
update(x);
}
int find(int x,int l,int r) //当前节点为x,查询区间[l..r]的元素个数
{
if( a[x].l == l && a[x].r == r ) return a[x].num;
int mid = ( a[x].l + a[x].r ) / 2;
if( r <= mid ) return find(2*x,l,r);
else if( l > mid ) return find(2*x+1,l,r);
else return find(2*x,l,mid) + find(2*x+1,mid+1,r);
}
int kth(int x,int k) //当前节点为x,查询第k大元素
{
if( a[x].l == a[x].r ) return a[x].l;
int mid = ( a[x].l + a[x].r ) / 2;
int s1 = a[2*x].num; //左区间的元素个数
int s2 = a[2*x+1].num; //右区间的元素个数
if( k <= s2 ) return kth(2*x+1,k); //k个最大的元素都在右区间
else return kth(2*x,k-s2); //去除最大的s2个,去剩下的找第k-s2大的元素
}