权值线段树总结

权值线段树是一种扩展的线段树,用于维护序列中每个元素的特定权值,如出现次数等。它可以实现插入、删除、查询排名、求第K小数等操作。本文总结了权值线段树在不同问题中的应用,包括洛谷P3369、HDU-1394、BZOJ1503等题目,展示了如何利用权值线段树解决逆序对、排名查询和工资档案管理等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

权值线段总结

权值线段树就是把线段树的每个点权,赋予一定的含义,比如数字出现的次数,数值前缀出现的次数,并用区间求和维护一个前缀信息,比如数字出现的次数,第K大等(不能实现区间第K大),前缀第K大等。

权值线段树优点:

能够比较容易实现平衡树的一系列操作

一个序列中,插入一个数,删除一个数,求值为数的排名,查询第K小的数,求比这个数小的数,求比这个数大的数。

上述操作都可以通过权值线段树实现。但是需要注意的是,上述操作数的范围如果过大,那么权值线段树将开不下,因为权值线段树存储的是节点的单点信息,也就是从1-N开始的序列,需要把所有操作的值进行存储,并进行离散化。这样权值线段树某种程度上说也就是离线的算法了。

下面对我所做过的权值线段树题目进行总结,提供关键代码

洛谷P3369 【模板】普通平衡树

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

1插入x数

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

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

4查询排名为x的数

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

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

 

思路:

离散化:

由于这个题目的x范围非常大,需要先把询问记录下来,并进行离散化

查询第K小的数:

这个可以利用权值线段树存储的是数出现的次数,维护区间内数字出现的次数,查询左右子树数字出现的次数,如果左儿子数字出现次数和是小于K,代表这个第K小在右子树,但是它在右子树的排名是K减去左子树儿子的个数,这样不断往下进行查找,当查找到单点的时候,就是第K小。

int Kth(int rt,int k){

   int l=tree[rt].l;

   int r=tree[rt].r;

   if(l==r){

      return l;

   }

   int mid=(l+r)>>1;

   if (tree[lson].sum>=k){

     return Kth(lson,k);

   }else {

     return Kth(rson,k-tree[lson].sum);

   }

查询x数的排名

可以利用权值线段树存储的数字个数的信息,查询[1,x-1]区间内部数字出现的个数+1,直接区间查询即可

x的前驱

我们可以求在[1,x-1]区间内部数字出现的次数记为k,那么第k大其实就是前面最靠近x的数,也就是前驱

x的后继:

我们可以求在[1,x]区间内部数字出现的的次数为k,那么第k+1大其实就是后面最靠近x的数,也就是后继。

 代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#define LL long long
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define per(i,j,k) for(int i=j;i>=k;i--)
#define pb push_back
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxx = 100005;
struct node{
  int l,r,sum;
}tree[maxx<<2];
int op[maxx];
int a[maxx];
vector<int>v;
int getid(int x){
  return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void buildtree(int rt,int l,int r){
    tree[rt].l=l;
    tree[rt].r=r;
    tree[rt].sum=0;
    if (l==r){
        return;
    }
    int mid=(l+r)>>1;
    buildtree(lson,l,mid);
    buildtree(rson,mid+1,r);
}
void update(int rt,int pos,int w){
   int l=tree[rt].l;
   int r=tree[rt].r;
   if (l==r){
     tree[rt].sum+=w;
     return;
   }
   int mid=(l+r)>>1;
   if(pos<=mid){
     update(lson,pos,w);
   }else{
     update(rson,pos,w);
   }
   tree[rt].sum=tree[lson].sum+tree[rson].sum;
}
int query(int rt,int ql,int qr){
    if(ql>qr)return 0;
    int l=tree[rt].l;
    int r=tree[rt].r;
    if(ql<=l && r<=qr){
       return tree[rt].sum;
    }
    int mid=(l+r)>>1;
    if (qr<=mid){
        return query(lson,ql,qr);
    }else if (ql>mid){
        return query(rson,ql,qr);
    }else {
        return query(lson,ql,mid)+query(rson,mid+1,qr);
    }
}
int Kth(int rt,int k){
   int l=tree[rt].l;
   int r=tree[rt].r;
   if(l==r){
      return l;
   }
   int mid=(l+r)>>1;
   if (tree[lson].sum>=k){
     return Kth(l
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值