【ZOJ 2112】 线段树套平衡树

题目大意:

给定一个数列,定义两种操作:

1、修改第n个数的值

2、求[l,r]区间内第k大的值。


方法:

线段树维护区间,treap树维护第k大(splay当然也可以)


树套树第一题。。没想到真的是每个线段树上的节点建一棵平衡树。。。。。

然后修改值就是在所有相关区间内的平衡树里,erase一个值再insert一个值。。。。简直恐怖

查询就是二分答案,因为每个区间都有一棵树,所以可以很快就可以在O(lgn)里算出这个区间内比查询数大的数字有多少个,满足区间加法,最后判断 比判断的这个数大的数是否 <= k,然后可以保证最后一个匹配边界值一定是数列里存在的数。


真是不自己调过一遍的程序没法彻底理解!!!

二分判断是否第k大和求第k大还是有细微差别的。

二分判断要考虑 key[x] == val 的情况 , 而求第k大只需要判断是否找的下去就行

 

#include <bits/stdc++.h>
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
#define CLR(arr) memset(arr,0,sizeof(arr))
using namespace std;
int N = 0 , M = 0;
const int maxn = 1020000;

int tmp = 0;
 
int root[maxn],treapCnt,key[maxn],priority[maxn], //root数组保存每个区间的根节点所对应的平衡数节点的的编号
childs[maxn][2],cnt[maxn],size[maxn];
	 
inline void update(int x){
	size[x] = size[ childs[x][0] ] + size[ childs[x][1] ] + cnt[x]; 
} 
	
inline void rotate(int& x, int t){
	int y = childs[x][t];
	childs[x][t] = childs[y][!t];
	childs[y][!t] = x;
	update(x);
	update(y);
	x = y;
}
	
void _insert(int& x, int k)
{
	if(x){
		if(key[x] == k)
			cnt[x]++;
		else
		{
			int t = key[x] < k;
			_insert(childs[x][t],k);
			if(priority[ childs[x][t] ] < priority[x])
				rotate(x,t);	
		}
	}
	else
	{
		x = treapCnt++;
		key[x] = k;
		cnt[x] = 1;
		childs[x][0] = childs[x][1] = 0;
		priority[x] = rand();
	}
	update(x); 
}
	
void _erase(int& x, int k)
{
	if(key[x] == k)
	{
		if(cnt[x] > 1)
			cnt[x]--;
		else
		{
			if(childs[x][0] == 0 && childs[x][1] == 0)
			{
				x = 0;
				return;
			}
			int t = priority[ childs[x][0] ] > priority[ childs[x][1] ];
			rotate(x,t);
			_erase(x,k);
		}
	} else {
		_erase(childs[x][key[x] < k] , k);
	}
	update(x);
}
	
	
void build(int k , int l , int r , int x , int num)
{
	_insert(root[k],num); //在每个所经区间的平衡树都加上这个结点
	int mid = (l + r) >> 1;
	if(l == r) return;
	if(x <= mid)
		build(lson(k),l,mid,x,num);
	else
		build(rson(k),mid+1,r,x,num);
} 

void change(int k, int l , int r ,int x ,int num,int y){
	_erase(root[k],y);  //对于所有的经过区间删旧添新
	_insert(root[k],num);  
	if(l == r) return;
	int mid = (l + r) >> 1;
	if (x <= mid)
		change(lson(k),l,mid,x,num,y);
	else
		change(rson(k),mid+1,r,x,num,y);
}


void finds(int k, int num){
	if(!k) return;
	if(key[k] <= num)
	{
		tmp += cnt[k] + size[ childs[k][0] ];
		finds( childs[k][1], num);
	}
	else
		finds( childs[k][0], num);
}
void query(int k , int l , int r , int x ,int y, int num)
{
	
	if(l == x && r == y){
		finds(root[k],num);
		return;
	}
	int mid = (l + r) >> 1;
	if(mid >= y)
		query(lson(k),l,mid,x,y,num);
	else if (mid < x)
		query(rson(k),mid+1,r,x,y,num);
	else
	{
		query(lson(k),l,mid,x,mid,num);
		query(rson(k),mid+1,r,mid+1,y,num);	
	} 
}
int a[maxn];
int main(){
	size[0] = 0;
	priority[0] = INT_MAX;
	int T = 0;
	scanf("%d",&T);
	while(T--){
		CLR(root);
		treapCnt = 1;
		int n = 0 , m = 0;
		scanf("%d %d",&n,&m);
		for (int i = 1 ; i <= n ; ++i) scanf("%d",&a[i]);
		for (int i = 1 ; i <= n ; ++i) build(1,1,n,i,a[i]);
		for (int i = 0 ; i < m ; ++i)
		{
			char s[3];
			int x = 0 , y = 0 , z = 0;
			scanf("%s",s);
			if(s[0] == 'C')
			{
				scanf("%d %d",&x,&y);
				change(1,1,n,x,y,a[x]);
				a[x] = y;
			}
			else
			{
				scanf("%d %d %d",&x,&y,&z);
				int l = 1 , r = 1000000000;
				while(l <= r)
				{
					int mid = (l + r) >> 1;
					tmp = 0; query(1,1,n,x,y,mid);
					if(tmp >= z) r = mid - 1;
					else l = mid + 1; 
				}
				printf("%d\n",l);
			}	
		}	 
	}
	return 0;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值