权值线段树
权值线段树
定义
在一些计数问题中,线段树用于维护值域(一段权值范围),这样的线段树也称为权值线段树。
排序与离散化
因为$sum$数组的下标是$x_{max}$,所以如果不做离散化的话,空间会出问题。
void Init() {
std::sort(a+1,a+cnt+1);
siz=std::unique(a+1,a+cnt+1)-a-1;
for(int i=1;i<=n;i++) {
if(op[i].opt!=4) {
op[i].x=std::lower_bound(a+1,a+siz+1,op[i].x)-a;
}
}
return;
}
插入和删除
插入和删除我们可以用一个函数实现,此时我们需要引入一个参数$delta$,通过将值设定为$\pm 1$来控制插入和删除。
void Update(int o,int l,int r,int x,int delta) {
sum[o]+=delta;
if(l==r) {
return;
}
if(x<=mid) {
Update(ls,l,mid,x,delta);
}
else {
Update(rs,mid+1,r,x,delta);
}
}
K-th(查询排名为k的数)
int Kth(int o,int l,int r,int x) {
if(l==r) {
return l;
}
if(x<=sum[ls]) {
return Kth(ls,l,mid,x);
}
else {
return Kth(rs,mid+1,r,x-sum[ls]);
}
}
查询某数x的排名(比x小的数的个数+1)
int Rank(int o,int l,int r,int x) {
if(l==r) {
return 1;
}
if(x<=mid) {
return Rank(ls,l,mid,x);
}
else {
return Rank(rs,mid+1,r,x)+sum[ls];
}
}
前驱与后继
两者都是用$Kth$和$Rank$实现的,但是具体细节会有一点点不同,这个不是很好说明,所以我用下面这个图来解释:
pre=a[Kth(1,1,siz,Rank(1,1,siz,op[i].x)-1)];
suc=a[Kth(1,1,siz,Rank(1,1,siz,op[i].x+1))];
板子
板子我是用$namespace$写的,放在这方便以后用。
#define N 100010
#define mid ((l+r)>>1)
#define ls (o<<1)
#define rs (o<<1|1)
int n,cnt,siz;
int a[N],sum[N<<2];
struct node {
int opt,x;
}op[N];
namespace Weight_Segment_Tree {
void Read() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
op[++tot].opt=1;
scanf("%d",&op[tot].x);
if(op[tot].opt!=4) {
a[++cnt]=op[tot].x;
}
if(i&1) {
op[++tot]=(node){4,(i>>1)+1};
}
}
return;
}
void Init() {
std::sort(a+1,a+cnt+1);
siz=std::unique(a+1,a+cnt+1)-a-1;
for(int i=1;i<=n;i++) {
if(op[i].opt!=4) {
op[i].x=std::lower_bound(a+1,a+siz+1,op[i].x)-a;
}
}
return;
}
void Update(int o,int l,int r,int x,int delta) {
sum[o]+=delta;
if(l==r) {
return;
}
if(x<=mid) {
Update(ls,l,mid,x,delta);
}
else {
Update(rs,mid+1,r,x,delta);
}
}
int Kth(int o,int l,int r,int x) {
if(l==r) {
return l;
}
if(x<=sum[ls]) {
return Kth(ls,l,mid,x);
}
else {
return Kth(rs,mid+1,r,x-sum[ls]);
}
}
int Rank(int o,int l,int r,int x) {
if(l==r) {
return 1;
}
if(x<=mid) {
return Rank(ls,l,mid,x);
}
else {
return Rank(rs,mid+1,r,x)+sum[ls];
}
}
void Solve() {
for(int i=1;i<=tot;i++) {
switch(op[i].opt) {
case 1:
Update(1,1,siz,op[i].x,1);
break;
case 2:
Update(1,1,siz,op[i].x,-1);
break;
case 3:
printf("%d\n",Rank(1,1,siz,op[i].x));
break;
case 4:
printf("%d %d\n",Kth(1,1,siz,op[i].x),a[Kth(1,1,siz,op[i].x)]);
break;
case 5:
printf("%d\n",a[Kth(1,1,siz,Rank(1,1,siz,op[i].x)-1)]);
break;
case 6:
printf("%d\n",a[Kth(1,1,siz,Rank(1,1,siz,op[i].x+1))]);
break;
}
}
return;
}
}