权值线段树(区间第k大)

权值线段树就是在线段树的基础上,将每一个点作为一个桶。区间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大的元素 
}         
发布了103 篇原创文章 · 获赞 6 · 访问量 7028

猜你喜欢

转载自blog.csdn.net/weixin_44316314/article/details/104155769