定义
权值线缎树就是特殊的线缎树,他的结构和普通线缎树一样,每个结点都是表示一段区间的范围(因此也需要开四倍空间),但是其每个结点存储的是该区间的数出现的次数。
有什么用
权值线段树维护的是桶(形象理解)
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;
}