权值线段树

前置知识:线段树

权值线段树

我们都知道,普通线段树是一种用来维护区间最值的数据结构。

而权值线段树,就是将序列各值出现的频次作为权值,再用线段树来维护值域的数据结构。

与其说是一种数据结构,不如说是线段树的一点技巧。

实际代码的话,用线段树维护桶数组就行了。

权值线段树重要作用是反映序列中元素大小,
如求第k大第k小问题。

因为本身与普通线段树差不多,所以就借用模板了((

code:

#include <bits/stdc++.h>
using namespace std;

const int N=3e5+10;

int n,k_;
int bucket_[N];

int tree[N<<1];

void push_up(int node)
{
    tree[node]=tree[node<<1]+tree[node<<1|1];
}

void build(int node,int start,int end)
{
    if(start==end)
    {
        tree[node]=bucket_[start];
        return ;
    }
    int mid=start+end>>1;
    int lnode=node<<1;
    int rnode=node<<1|1;
    build(lnode,start,mid);
    build(rnode,mid+1,end);
    push_up(node);
}

void update(int node,int start,int end,int k,int val)//大小为val的数多k个,相当于单点修改
{
    if(start==end)
    {
        tree[node]+=k;
        return ;
    }
    int mid=start+end>>1;
    int lnode=node<<1;
    int rnode=node<<1|1;
    if(val<=mid) update(lnode,start,mid,k,val);
    else update(rnode,mid+1,end,k,val);

    push_up(node);
}

int query(int node,int start,int end,int val)//查询数字val有多少个,相当于单点查询
{
    if(start==end) return tree[node];
    int mid=start+end>>1;
    int lnode=node<<1;
    int rnode=node<<1|1;
    if(val<=mid) return query(lnode,start,mid,val);
    else return query(rnode,mid+1,end,val);
    push_up(node);
}

int query_kth(int node,int start,int end,int k)//查询第k小
{
    if(start==end) return start;
    int mid=start+end>>1;
    int lnode=node<<1;
    int rnode=node<<1|1;
    if(tree[lnode]>=k) return query_kth(lnode,start,mid,k);//如果左子树的权值大于k,证明第k小值左子树
    else return query_kth(rnode,mid+1,end,k-tree[lnode]);//进入右子树时,整个区间的第k小相当于右区间的第(k-左区间)小,记得减去左子树的值
}

int query_kthbig(int node,int start,int end,int k)//查询第k大
{
    if(start==end) return start;
    int mid=start+end>>1;
    int lnode=node<<1;
    int rnode=node<<1|1;
    if(tree[rnode]>=k) return query_kthbig(rnode,mid+1,end,k);//若右子树的权值大于k,证明第k大值在右子树
    else return query_kthbig(lnode,start,mid,k-tree[rnode]);//进入左子树时,记得减去右区间
}

int main()
{
    scanf("%d%d",&n,&k_);
    int maxn=0,tot=0;
    for(int i=1; i<=n; i++)
    {
        int x;
        scanf("%d",&x);
        maxn=maxn>=x?maxn:x;
        bucket_[x]++;
    }
    build(1,1,maxn);
    cout<<query_kth(1,1,maxn,k_);
    return 0;
}

线段树细分的种类很多,像线段树分裂,主席树,时间分治,动态开点权值线段树合并

都是OI路上同行的知识点,需要更多时间和更深入的体会。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值