权值线段树

权值线段树

定义

在一些计数问题中,线段树用于维护值域(一段权值范围),这样的线段树也称为权值线段树

排序与离散化

因为$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
",Rank(1,1,siz,op[i].x));
					break;
				case 4:
					printf("%d %d
",Kth(1,1,siz,op[i].x),a[Kth(1,1,siz,op[i].x)]);
					break;
				case 5:
					printf("%d
",a[Kth(1,1,siz,Rank(1,1,siz,op[i].x)-1)]);
					break;
				case 6:
					printf("%d
",a[Kth(1,1,siz,Rank(1,1,siz,op[i].x+1))]);
					break;
			}
		}
		return;
	}
}
原文地址:https://www.cnblogs.com/luoshui-tianyi/p/13469441.html