权值线段树 基础入门知识详解

权值线段树

学习权值线段树,首先要了解线段树是什么。如果不会的可以先学习一下。

是什么

权值线段树,顾名思义是一棵线段树。
但它和普通线段树不同:
线段树,每个节点用来维护一段区间的最大值或总和等。
权值线段树,相当于一个桶,每个节点用来表示一个区间的数出现的次数

为什么要用它

我们可以用它来维护一段区间的数出现的次数,从它的定义上来看,它可以快速计算一段区间的数的出现次数。
此外,它还有一个重要功能,在于它可以快速找到第k大或第k小值,下面会做详细解释。
其实,它就是一个桶,桶能做到的它都可以用更快的速度去完成。

基本操作

添加

和普通线段树类似,递归到叶子节点时给f[v]+1
以下代码要添加的数是x,也就是x出现的次数+1

	void add(int l,int r,int v,int x)
	{
		if(l==r) f[v]++;
		else
		{
			int mid=(l+r)/2;
			if(x<=mid) add(l,mid,v*2,x); else add(mid+1,r,v*2+1,x);
			f[v]=f[v*2]+f[v*2+1];
		{
	}

查询一个数出现的次数

如添加操作,递归到叶子节点时f[v]的值即为所求次数。
以下代码要查询的数是x

	int find(int l,int r,int v,int x)
	{
		if(l==r) return f[v];
		else
		{
			int mid=(l+r)/2;
			if(x<=mid) return find(l,mid,v*2,x); else return find(mid+1,r,v*2+1,x);
		}
	}

查询一段区间的数出现的次数

与线段树查询同理,不断递归二分。
以下代码要查询的区间是[x,y]

	int find(int l,int r,int v,int x,int y)
	{
		if(l==x&&r==y) return f[v];
		else
		{
			int mid=(l+r)/2;
			if(y<=mid) return find(l,mid,v*2,x,y);
			else if(x>mid) return find(mid+1,r,v*2+1,x,y);
			else return find(l,mid,v*2,x,mid)+find(mid+1,r,v*2+1,mid+1,y);
		}
	}

查询所有数的第k大值

这是权值线段树的核心,思想如下:
到每个节点时,如果右子树的总和大于等于k,说明第k大值出现在右子树中,则递归进右子树;否则说明此时的第k大值在右子树中,则递归进左子树,注意:此时要将k的值减去右子树的总和。
为什么要减去?
如果我们要找的是第7大值,右子树总和为474=3,说明在该节点的第7大值在左子树中是第3大值。
最后一直递归到只有一个数时,那个数就是答案。

	int kth(int l,int r,int v,int k)
	{
		if(l==r) return l;
		else
		{
			int mid=(l+r)/2,s1=f[v*2],s2=f[v*2+1];
			if(k<=s2) return kth(mid+1,r,v*2+1,k); else return kth(l,mid,v*2,k-s2);
		}
	}

总结

权值线段树的基础知识就是这些了,相信你都学会了。
希望你能够灵活变通,在今后的OI生涯中更上一层楼。
如果你想学习进阶知识,可以看看【主席树】可持久化线段树

posted @   AnAn_119  阅读(339)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示