权值线段树1
一.权值线段树与线段树的区别:
-
权值线段树维护数的个数,数组下标代表整个值域(如果值域太大,可以离散化,后面会有介绍)
-
线段树则是直接维护每个数
二.权值线段树的用处
1.寻找第K大(整个区间,即左边界为1,右边界为n)
2.逆序对(呵呵归并也能求)
3.最大差&最小差(??!)
4..............
三.权值线段树的具体实现
没什么好说的,直接上代码(丑):
建树(build):
inline void build(int root,int L,int R)
{
tree[root].l=L;
tree[root].r=R;
if(L==R)
{
//初始化
return;
}
int mid=(L+R)>>1;
build(root<<1,L,mid);//建左儿子
build(root<<1|1,mid+1,R);//建右儿子
}
更新(update):
inline void update(int root,int t)
{
if(tree[root].l==tree[root].r)
{
//更新数据
return;
}
int mid=(tree[root].l+tree[root].r)>>1;
if(t<=mid) update(root<<1,t);//在左儿子中
else update(root<<1|1,t);//在右儿子中
//维护一下(push_up)
}
这两个部分与普通线段树没什么两样啊----------
询问整体第k大(query):
在线段树上进行二分:
先看左子树数的个数,设其个数为f.
如果f>=t递归进入左子树寻找
如果f<k递归进入右子树寻找第f-k大
即整体二分
//询问整个区间第t大(这里t代表k)//tree[root].s代表tree[root].l至tree[root].r值域中数的个数总和
inline int query(int root,int t)
{
if(tree[root].l==tree[root].r)
return tree[root].l;//由于数组下标维护的是值域,直接返回其下标
if(t<=tree[root<<1].s) return query(root<<1,t);//在左子树中
else return query(root<<1|1,t-tree[root<<1].s);//在右子树中,记得减去左子树个数
}
四.例题
仔细模拟即可。
直接上AC代码:
#include<bits/stdc++.h>
#define N 200005
using namespace std;
int m,n,k;
int a[N],b[N],u[N];
struct MM{
int l,r,s;
}tree[N<<2];
inline void build(int root,int L,int R)
{
tree[root].l=L;
tree[root].r=R;
if(L==R) return;
int mid=(L+R)>>1;
build(root<<1,L,mid);
build(root<<1|1,mid+1,R);
}
inline void update(int root,int t)
{
if(tree[root].l==tree[root].r)
{
tree[root].s++;//个数加一
return;
}
int mid=(tree[root].l+tree[root].r)>>1;
if(t<=mid) update(root<<1,t);
else update(root<<1|1,t);
tree[root].s=tree[root<<1].s+tree[root<<1|1].s;
}
inline int query(int root,int t)
{
if(tree[root].l==tree[root].r)
return tree[root].l;
if(t<=tree[root<<1].s) return query(root<<1,t);
else return query(root<<1|1,t-tree[root<<1].s);
}
int main()
{
cin>>m>>n;
for(int i=1;i<=m;i++)
{
cin>>a[i];
b[i]=a[i];
}
for(int i=1;i<=n;i++)
cin>>u[i];
sort(b+1,b+m+1);
int s=unique(b+1,b+m+1)-(b+1);//离散化(若值域很大),s是数组b中不重复的数的个数
build(1,1,s);//依s建树
int h=0;
while(n!=h)
{
h++;
for(int i=u[h-1]+1;i<=u[h];i++)
{
int v=lower_bound(b+1,b+s+1,a[i])-b;//v是a[i]在数组b中所处的位置(注意之前数组b排了序)
update(1,v);
}
cout<<b[query(1,++k)]<<endl;
}
return 0;
}
蒟蒻第一次写博客,请大佬们多多提建议
NO PAIN NO GAIN
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现