BZOJ3224_yvj 1728 普通平衡树(权值线段树)

题意:

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入x数
  2. 删除x数(若有多个相同的数,因只删除一个)
  3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
  4. 查询排名为x的数
  5. 求x的前驱(前驱定义为小于x,且最大的数)
  6. 求x的后继(后继定义为大于x,且最小的数)

思路:

平衡树还不会,后面补上。先用权值线段树来做。
权值线段树不了解的先看:权值线段树
插入删除操作,用一个更新操作完成,将其对应的出现次数+1或-1即可。
查询数x的排名:转化为找x前面有多少数,然后+1。找x前面有多少数,即在[1, x-1]上线段树求和。
查询第k小的数:找根节点的左孩子有多少数,如果>=k,那么就在左孩子中找,反之在右孩子中找,这样递归下去,到叶子节点就找到了。
查询x的前驱:根节点分为两个区间,【l, mid】和【mid+1, r】,如果x > mid+1并且右孩子非空,那么x的前驱可能在右孩子中,反之在左孩子中。因为前驱是要找比x小的最大数,所以每次优先在右孩子中找,找不到再去左孩子。
上面有个可能,举个例子:假设x = mid+2,而mid+1处是空的(可能被删除了),那么这时候前驱就在左孩子中了。
查询x的后继和前驱类似。

// 权值线段树 
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson l, mid, rt<<1 
#define rson mid+1, r, rt<<1|1
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 100000+5;
int Tree[maxn<<2], X[maxn];
struct Operation{
	int p, v;
}Op[maxn];

// 更新操作(插入删除) 
void update(int k, int l, int r, int rt, int val){
	Tree[rt]+= val;
	if(l == r) return;
	int mid = (l+r) >> 1;
	if(k <= mid) update(k, lson, val);
	else update(k, rson, val);
}

// 找val的排名: 求[1, val-1]的和 
int rank(int la, int rb, int l, int r, int rt){
	if(la > r||rb < l) return 0;
	if(la <= l&&rb >= r) return Tree[rt];
	int mid = (l+r) >> 1, ans = 0;
	if(la <= mid) ans+= rank(la, rb, lson);
	if(rb > mid) ans+= rank(la, rb, rson);
	return ans;
}

// 找第k小 
int find_kth(int l, int r, int rt, int k){
	if(l == r) return l;
	int mid = (l+r) >> 1;
	if(Tree[rt<<1] >= k) return find_kth(lson, k);
	else return find_kth(rson, k-Tree[rt<<1]);
}

int find_rightest(int l, int r, int rt){
	if(l == r) return l;
	int mid = (l+r) >> 1;
	if(Tree[rt<<1|1]) return find_rightest(rson);
	else return find_rightest(lson); 
}
// 找val的前驱
int pre(int l, int r, int rt, int val){
	if(r < val){
		if(Tree[rt]) return find_rightest(l, r, rt);
		return 0;
	}
	int mid = (l+r) >> 1, ans;
	if(val > mid+1&&Tree[rt<<1|1]&&(ans = pre(rson, val))) return ans;
	return pre(lson, val);
}

int find_leftest(int l, int r, int rt){
	if(l == r) return l;
	int mid = (l+r) >> 1;
	if(Tree[rt<<1]) return find_leftest(lson);
	else return find_leftest(rson); 
}
// 找val的后继 
int nex(int l, int r, int rt, int val){
	if(val < l){
		if(Tree[rt]) return find_leftest(l, r, rt);
		return 0;
	}
	int mid = (l+r) >> 1, ans;
	if(val < mid&&Tree[rt<<1]&&(ans = nex(lson, val))) return ans;
	return nex(rson, val);
}

int main()
{
	int n;
	while(scanf("%d",&n) == 1&&n){
		memset(Tree, 0, sizeof(Tree));
		int tot = 0;
		for(int i = 1; i <= n; ++i){
			scanf("%d%d", &Op[i].p,&Op[i].v);
			if(Op[i].p == 1) X[++tot] = Op[i].v;
		}
		//printf("n = %d, tot = %d\n",n,tot);
		sort(X+1, X+tot+1);
		int p, v, t;
		for(int i = 1; i <= n; ++i){
			p = Op[i].p; 
			v = Op[i].v;
			int k = lower_bound(X+1, X+tot+1, v) - X;
			switch (p){
				case 1: update(k, 1, n, 1, 1); break;
				case 2: update(k, 1, n, 1, -1); break;
				case 3: printf("%d\n", 1+rank(1, k-1, 1, n, 1)); break;	// 找x的排名 
				case 4: t = find_kth(1, n, 1, v); printf("%d\n", X[t]); break;
				case 5: t = pre(1, n, 1, k); printf("%d\n", X[t]); break;
				case 6: k = upper_bound(X+1, X+tot+1, v) - X;
						t = nex(1, n, 1, k-1); printf("%d\n", X[t]);
			}
		}
	}

	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值