权值线段树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);//在右子树中,记得减去左子树个数
}

 

四.例题

黑匣子https://www.luogu.org/problemnew/show/P1801  

仔细模拟即可。

直接上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;
}

 蒟蒻第一次写博客,请大佬们多多提建议

 

转载于:https://www.cnblogs.com/zmyzmy/p/9529234.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值