权值线段总结
权值线段树就是把线段树的每个点权,赋予一定的含义,比如数字出现的次数,数值前缀出现的次数,并用区间求和维护一个前缀信息,比如数字出现的次数,第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