BZOJ 3196 二逼平衡树 树套树(线段树套Treap)

题目大意:

写一种数据结构,他可以:

1.查询k在区间内的排名。

2.查询区间内排名为k的值

3.修改某一个值。

4.求k在区间内的前驱。

5.求k在区间内的后继。

思路:本来以为有什么只有神犇才知道的神一般的数据结构来维护它,问了别人之后,发现只是树套树。据说怎么套都行。我见识鄙陋,就只能线段树套Treap了。

这也是第一次写树套树,还1A了,有点开心。

写树套树,一定要确定自己对这两个树及其熟练,加上少量精细的思考,就可以完成树套树。(我只是弱渣,求神犇别D)

具体实现:第一层是线段树,第二层是Treap。由于每个线段树的节点代表实际的一段区间,这样就可以在每个线段树的节点下面接一个Treap,维护这段区间,并可以在O(logn)的时间之内求出这段区间的各种信息。

考虑第一种操作:考虑线段树的工作原理,对于整段区间,就直接在这个区间的Treap上进行操作,然后返回结果。对于不整的区间,递归解决左半部分和右半部分,然后加起来返回。

第二种操作:对于一整段的区间可以直接在Treap上找区间第K大,但是对于不整的区间就无能为力了。所以没办法只能在套一层logn,二分查找这个值。对于二分的mid值,查找这个值在区间内的排名,这显然满足二分性质。要好好讨论一下这个问的边界条件。

第三种操作:递归向下修改,没经过一个线段树的节点,就把这个节点下面接的Treap减去原来位置的那个值,然后加上那个位置变成的值。

最后两种操作:对于整区间,直接返回Treap返回的前驱,后继。不整的区间,递归求出左半部分,右半部分,取最小或最大值,然后返回。

CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 50010
#define INF 1e9
#define LEFT (pos << 1)
#define RIGHT (pos << 1|1)
#define SIZE(x) ((x == NULL) ? 0:x->size)
using namespace std;

struct Complex{
	int val,cnt,size,random;
	Complex *son[2];

	Complex() {
		son[0] = son[1] = NULL;
		cnt = size = 1;
		random = rand();
	}
	int Compare(int x) {
		if(x == val)	return -1;
		return x > val;
	}
	void Maintain() {
		size = cnt;
		if(son[0] != NULL)	size += son[0]->size;
		if(son[1] != NULL)	size += son[1]->size;
	}
};

int cnt,asks;
int src[MAX];

Complex *tree[MAX << 2];

void Pretreatment();

void BuildTree(int l,int r,int pos);
int GetRank(int l,int r,int x,int y,int pos,int k);
int GetKth(int x,int y,int pos,int k);
void Modify(int l,int r,int aim,int pos,int c);
int FindPred(int l,int r,int x,int y,int pos,int k);
int FindSucc(int l,int r,int x,int y,int pos,int k); 

inline void Rotate(Complex *&a,bool dir);
void Insert(Complex *&a,int x);
void Delete(Complex *&a,int x);
int GetRank(Complex *a,int k);
int FindPred(Complex *a,int x);
int FindSucc(Complex *a,int x);

int main()
{
	Pretreatment();
	cin >> cnt >> asks;
	for(int i = 1;i <= cnt; ++i)
		scanf("%d",&src[i]);
	BuildTree(1,cnt,1);
	for(int flag,i = 1;i <= asks; ++i) {
		scanf("%d",&flag);
		int x,y,z;
		if(flag == 1) {
			scanf("%d%d%d",&x,&y,&z);
			printf("%d\n",GetRank(1,cnt,x,y,1,z) + 1);
		}
		if(flag == 2) {
			scanf("%d%d%d",&x,&y,&z);
			printf("%d\n",GetKth(x,y,1,z));
		}
		if(flag == 3) {
			scanf("%d%d",&x,&y);
			Modify(1,cnt,x,1,y);
			src[x] = y;
		}
		if(flag == 4) {
			scanf("%d%d%d",&x,&y,&z);
			printf("%d\n",FindPred(1,cnt,x,y,1,z));
		}
		if(flag == 5) {
			scanf("%d%d%d",&x,&y,&z);
			printf("%d\n",FindSucc(1,cnt,x,y,1,z));
		}
	}
	return 0;
}

void Pretreatment()
{
	memset(tree,NULL,sizeof(tree));
}

void BuildTree(int l,int r,int pos)
{
	for(int i = l;i <= r; ++i)
		Insert(tree[pos],src[i]);
	if(l == r)	return ;
	int mid = (l + r) >> 1;
	BuildTree(l,mid,LEFT);
	BuildTree(mid + 1,r,RIGHT);
}

int GetRank(int l,int r,int x,int y,int pos,int k)
{
	if(l == x && y == r)
		return GetRank(tree[pos],k);
	int mid = (l + r) >> 1;
	if(y <= mid)	return GetRank(l,mid,x,y,LEFT,k);
	if(x > mid)		return GetRank(mid + 1,r,x,y,RIGHT,k);
	int left = GetRank(l,mid,x,mid,LEFT,k);
	int right = GetRank(mid + 1,r,mid + 1,y,RIGHT,k);
	return left + right;
}

int GetKth(int x,int y,int pos,int k)
{
	int L = 0,R = INF,re;
	while(L <= R) {
		int mid = (L + R) >> 1;
		int temp = GetRank(1,cnt,x,y,1,mid);
		if(temp < k)	L = mid + 1;
		else	R = mid - 1;
	}
	int temp = GetRank(1,cnt,x,y,1,L);
	if(temp >= k)	L = FindPred(1,cnt,x,y,1,L);
	return L;
}

void Modify(int l,int r,int aim,int pos,int c)
{
	Delete(tree[pos],src[aim]);
	Insert(tree[pos],c);
	if(l == r)	return ;
	int mid = (l + r) >> 1;
	if(aim <= mid)	Modify(l,mid,aim,LEFT,c);
	else	Modify(mid + 1,r,aim,RIGHT,c);
}

int FindPred(int l,int r,int x,int y,int pos,int k)
{
	if(l == x && y == r)
		return FindPred(tree[pos],k);
	int mid = (l + r) >> 1;
	if(y <= mid)	return FindPred(l,mid,x,y,LEFT,k);
	if(x > mid)		return FindPred(mid + 1,r,x,y,RIGHT,k);
	int left = FindPred(l,mid,x,mid,LEFT,k);
	int right = FindPred(mid + 1,r,mid + 1,y,RIGHT,k);
	return max(left,right);
}

int FindSucc(int l,int r,int x,int y,int pos,int k)
{
	if(l == x && y == r)
		return FindSucc(tree[pos],k);
	int mid = (l + r) >> 1;
	if(y <= mid)	return FindSucc(l,mid,x,y,LEFT,k);
	if(x > mid)		return FindSucc(mid + 1,r,x,y,RIGHT,k);
	int left = FindSucc(l,mid,x,mid,LEFT,k);
	int right = FindSucc(mid + 1,r,mid + 1,y,RIGHT,k);
	return min(left,right);
}

////////////////////////////////Treap///////////////////////////

inline void Rotate(Complex *&a,bool dir)
{
	Complex *k = a->son[!dir];
	a->son[!dir] = k->son[dir];
	k->son[dir] = a;
	a->Maintain(),k->Maintain();
	a = k;
}

void Insert(Complex *&a,int x)
{
	if(a == NULL) {
		a = new Complex();
		a->val = x;
		return ;
	}
	int dir = a->Compare(x);
	if(dir == -1)
		a->cnt++;
	else {
		Insert(a->son[dir],x);
		if(a->son[dir]->random > a->random)
			Rotate(a,!dir);
	}
	a->Maintain();
}

void Delete(Complex *&a,int x)
{
	int dir = a->Compare(x);
	if(dir != -1)
		Delete(a->son[dir],x);
	else {
		if(a->cnt > 1)	a->cnt--;
		else {
			if(a->son[0] == NULL)	a = a->son[1];
			else if(a->son[1] == NULL)	a = a->son[0];
			else {
				bool _dir = (a->son[0]->random > a->son[1]->random);
				Rotate(a,_dir);
				Delete(a->son[_dir],x);
			}
		}
	}
	if(a != NULL)	a->Maintain();
}

int GetRank(Complex *a,int k)
{
	if(a == NULL)	return 0;
	if(k <= a->val)	return GetRank(a->son[0],k);
	return SIZE(a->son[0]) + a->cnt + GetRank(a->son[1],k);
}

int FindPred(Complex *a,int x)
{
	if(a == NULL)	return -INF;
	if(a->val >= x)	return FindPred(a->son[0],x);
	return max(a->val,FindPred(a->son[1],x));
}

int FindSucc(Complex *a,int x)
{
	if(a == NULL)	return INF;
	if(a->val <= x)	return FindSucc(a->son[1],x);
	return min(a->val,FindSucc(a->son[0],x));
}
时间: 2024-08-08 12:45:31

BZOJ 3196 二逼平衡树 树套树(线段树套Treap)的相关文章

[BZOJ 3196] 二逼平衡树 树状数组套主席树

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3357  Solved: 1326[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为

BZOJ 3196 二逼平衡树 树套树

题目大意:...BZOJ挂了自己看去 好吧既然BZOJ挂了我还是贴上来吧0.0 破服务器 维护一种数据结构,提供下列操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) 其实一开始觉得这题是划分树主席树之类的 然后去了解了一下发现完全写不了... 后来才知道原来是树套树 以前想过线段树套树状数组 这数据范围别说树套树了连树状数组都开不开 正解应该是

BZOJ 3196 二逼平衡树 线段树+treap

题意:链接 方法:线段树+treap的模板题 题解: 首先是对于整个树的定义,其实treap部分并没有什么区别,只不过是单root改变为多root而已. #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define lson l,mid,rt<<1 #define rson mid+1,

BZOJ 3196 二逼平衡树

Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,m 表示长度为n的有序序列和m个操作第二行有n个数,表示有序序列下面有m行,opt表示操作标号若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名

3196. 二逼平衡树【线段树套splay】

Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱定义为小于x,且最大的数) 5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,m 表示长度为n的有序序列和m个操作 第二行有n个数,表示有序序列 下面有m行,opt表示操作标号 若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间

BZOJ3196 Tyvj1730 二逼平衡树

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3196 Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,m 表示长度为n的有序序列和m个操作第二行有n个数,表示有序

bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为大于x,且最小的数) Input 第一行两个数 n,

BZOJ 3196: Tyvj 1730 二逼平衡树( 树套树 )

这道题做法应该很多吧.... 我用了线段树套treap.... -------------------------------------------------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<cstdlib> #include<ios

bzoj 3196 &amp;&amp; luogu 3380 JoyOI 1730 二逼平衡树 (线段树套Treap)

链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3196 题面; 3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 6372  Solved: 2406[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为