权值线缎树

定义

权值线缎树就是特殊的线缎树,他的结构和普通线缎树一样,每个结点都是表示一段区间的范围(因此也需要开四倍空间),但是其每个结点存储的是该区间的数出现的次数。

有什么用

权值线段树维护的是桶(形象理解)
1、快速计算一段区间的数的出现次数。
2、快速找到第k大或第k小值。

基操

1、注意题目中数据范围一般较大,数的值一般高达1e9左右,如果就这么插入权值线缎树会爆内存(高达4e9,哪顶的住),这就需要用到离散化了因此一般是先对序列离散化,再将其插入权值线缎树。

//离散化
    vector<ll>a(n);
    for(auto& it:a)cin>>it;
    sort(a.begin(),a.end());
    a.erase(unique(a.begin(),a.end()),a.end())

再定义一个函数用以获取数在离散化后的序列中的id值。

int gid(ll x)
{
    return lower_bound(vt.begin(),vt.end(),x)-vt.begin()+1;
}

2、离散化之后就是往树内插入数了

const int MAXN = 2e5+5;
ll t[MAXN<<2];//用t数组来存树的结点值,记得开四倍空间
void update(int l,int r,int rt,int id)
{
    if(l==r)
    {
        t[rt]++;
        return;
    }
    int mid=l+((r-l)>>1);//这个括号一定要加,坑了很多次了,位运算优先级低
    if(id<=mid)
    update(l,mid,rt<<1,id);
    else
    update(mid+1,r,rt<<1|1,id);
    t[rt] = t[rt<<1]+t[rt<<1|1];
    return;
}

3、既然是线缎树,他也有查询操作

①查询某个数出现的次数

//求某个数的个数
int query(int l,int r,int rt,int id)
{
    if(l==r)return t[rt];
    int mid=l+((r-l)>>1);
    if(id <= mid) return query(l,mid,rt<<1,id);
    else return query(mid+1,r,rt<<1|1,id);
}

②查询某个区间内所有数出现的总次数
(这个也包括了求单个数,只要L=R=id即可)

//求某个区间数的个数
int query(int l,int r,int rt,int L,int R)
{
    if(L<=l&&r<=R)return t[rt];
    int mid=l+((r-l)>>1);
    int ans=0;
    if(L <= mid) ans += query(l,mid,rt<<1,L,R);
    if(R > mid) ans += query(mid+1,r,rt<<1|1,L,R);
    return ans;
}

4、查询第k大的数

//求第k大的数
int kth(int l,int r,int rt,int k)
{
    if(l==r)return a[l-1];
    int mid=l+((r-l)>>1);
    int rsum=t[rt<<1|1];
    if(k<=rsum)return kth(mid+1,r,rt<<1|1,k);
    else return kth(l,mid,rt<<1,k-rsum);   
}

同理查询第k小的数也就知道怎么做了。

这里附上整个板子,查询第k小的数和4大同小异,就不给出了。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <string>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <map>
using namespace std;
#define ms(a, x) memset(a, x, sizeof(a))
#define fore(i, a, n) for (long long i = a; i < n; i++)
#define ford(i, a, n) for (long long i = n - 1; i >= a; i--)
#define si(a) scanf("%d", &a)
#define sl(a) scanf("%lld", &a)
#define sii(a, b) scanf("%d%d", &a, &b)
#define siii(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define sll(a, b) scanf("%lld%lld", &a, &b)
#define slll(a, b, c) scanf("%lld%lld%lld", &a, &b, &c)
#define debug(a) cout << a << endl
#define pr(a) printf("%d ",a)
#define endl '\n'
#define pi acos(-1.0)
#define tr t[root]
#define lson t[root << 1]
#define rson t[root << 1 | 1]
#define IO ios::sync_with_stdio(false), cin.tie(0)
#define ull unsigned long long
#define ll long long
const double eps = 1e-8;
inline int sgn(const double &x) { return x < -eps ? -1 : x > eps; }
const int inf=0x3f3f3f3f;
const int MAXN = 2e5+5;
ll sum[MAXN<<2];
vector<ll>a;
ll t[MAXN<<2];
int gid(ll x)
{
    return lower_bound(a.begin(),a.end(),x)-a.begin()+1;
}
void update(int l,int r,int rt,int id)
{
    if(l==r)
    {
        t[rt]++;
        return;
    }
    int mid=l+((r-l)>>1);
    if(id<=mid)
    update(l,mid,rt<<1,id);
    else
    update(mid+1,r,rt<<1|1,id);
    t[rt] = t[rt<<1]+t[rt<<1|1];
    return;
}
//求某个数的个数
int query(int l,int r,int rt,int id)
{
    if(l==r)return t[rt];
    int mid=l+((r-l)>>1);
    if(id <= mid) return query(l,mid,rt<<1,id);
    else return query(mid+1,r,rt<<1|1,id);
}
//求某个区间数的个数
int query(int l,int r,int rt,int L,int R)
{
    if(L<=l&&r<=R)return t[rt];
    int mid=l+((r-l)>>1);
    int ans=0;
    if(L <= mid) ans += query(l,mid,rt<<1,L,R);
    if(R > mid) ans += query(mid+1,r,rt<<1|1,L,R);
    return ans;
}
//求第k大的数
int kth(int l,int r,int rt,int k)
{
    if(l==r)return a[l-1];
    int mid=l+((r-l)>>1);
    int rsum=t[rt<<1|1];
    if(k<=rsum)return kth(mid+1,r,rt<<1|1,k);
    else return kth(l,mid,rt<<1,k-rsum);   
}
int main()
{
    int n;
    si(n);
    a.resize(n);
    for(auto& it:a)cin>>it;
    vector<ll>b(a);
    sort(a.begin(),a.end());
    a.erase(unique(a.begin(),a.end()),a.end());
    int sz=a.size();
    for(int i=0;i<b.size();++i)
    {
        update (1,sz,1,gid(b[i]));
    }
    int x;
    si(x);
    cout<<query(1,sz,1,gid(x))<<endl;
    int l,r;
    sii(l,r);
    cout<<query(1,sz,1,gid(l),gid(r))<<endl;
    int k;
    si(k);
    cout<<kth(1,sz,1,k)<<endl;
}

例题:HDU - 6609 Find the answer

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值