1
30
2016
0

树套树学习笔记~ +【tyvj1730】二逼平衡树

3196: Tyvj 1730 二逼平衡树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1363  Solved: 579
[Submit][Status][Discuss]

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]的排名
若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k
若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继

Output

对于操作1,2,4,5各输出一行,表示查询结果

Sample Input

9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5

Sample Output

2
4
3
4
9

HINT

 

1.n和m的数据范围:n,m<=50000


2.序列中每个数的数据范围:[0,1e8]


3.虽然原题没有,但事实上5操作的k可能为负数

【树套树】

线段树套平衡树还是很好理解的~

无非是要求在区间上进行平衡树的操作时用到

因为一个区间(线段树结点)对应的是一棵平衡树

所以在线段树上找到该区间后进行平衡树操作就好!

 

【switch语句:】
1\记得写break!

2\变量定义在switch外,否则会出现错误:error:jump to case label

switch(q){
	case 1:...
	       break;
	case 2:...
	       break;
	case 3:...
	       break;
	default:...
		break;
}

【分析】Orz Regina8023:

以位置为关键字建一棵线段树,在线段树的每个节点上再建一棵以权值为关键字的treap即可。

 

五个操作的实现方法

1.查询k在区间内排名(Getrank)

首先在线段树中找到区间的位置(Getrank),然后在找到的每一小段区间中的treap上找比k大的数的个数(Askrank),输出个数+1即可

 

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

首先二分答案(Getnum),然后用操作1中的Getrank找到目前二分出来的数在区间的排名,如果排名<=k,则在右半部分继续找,否则在左半部分

 

查询区间内排名为k的值与在一棵Treap上查找排名为k的元素不同!注意有多个区间,即多棵树

通过二分转化为查询k在区间内排名,这个问题好解决:分别在每个区间内查询比k小的元素,个数+1即可

 

 

3.修改某一位置上的数值(Modify)

这个操作等价于删除一个数,再插入一个数。但是要在线段树中每一个包含此数的区间都执行这两个操作。

 

4.查询前驱(Getpre)

在线段树中找到区间的位置,在每一个区间中找小于k的最大数,最后取最大值即可

 

5.查询后继(Getnext)

在线段树中找到区间的位置,在每一个区间中找小于k的最小数,最后取最小值即可

 

注意:

1.对于重复的数值的处理方法是在treap上维护一个weight,表示与当前结点的权值相同的数有几个。

 

2.查询操作要给全出口,否则就会死循环。。


MY CODE:

#include<bits/stdc++.h>
#include<ctime>
#define F(i,s,t) for(int i=(s);i<=(t);i++)
#define D(i,s,t) for(int i=(t);i>=(s);i--)
using namespace std;
const int N=3e6;
const int INF=1<<30;
typedef long long LL;
inline int getint()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
struct treap{int key,pro,weight,l,r,siz;}t[N];
inline void pushup(int p)
{
	int l=t[p].l,r=t[p].r;
	t[p].siz=t[l].siz+t[r].siz+t[p].weight;
}
/*—————Zig/Zag—————*/ 
inline void zig(int &p)
{
	int x=t[p].r;
	t[p].r=t[x].l;
	t[x].l=p;
	t[x].siz=t[p].siz;
	pushup(p);
	p=x;
}
inline void zag(int &p)
{
	int y=t[p].l;
	t[p].l=t[y].r;
	t[y].l=p;
	t[y].siz=t[p].siz;
	pushup(p);
	p=y;
}
/*———————insert/delete——————*/ 
int n,m,x[N],q;
int tot=0,ans,num,rt[N];
inline void newnode(int &p,int data)
{
	p=++tot;
	t[p].weight=1;
	t[p].siz=1;
	t[p].key=data;
	t[p].pro=rand();//%((int)1e8);
	t[p].l=t[p].r=0;
}
inline void insert(int &p,int data)
{
	if(!p){	newnode(p,data);return;}
	t[p].siz++;
	if(t[p].key==data){t[p].weight++;return;}
	if(t[p].key>data){
		int l=t[p].l;
		insert(l,data);
		if(t[l].pro<t[p].pro) zag(p);
	}else{
		int r=t[p].r;
		insert(r,data);
		if(t[r].pro<t[p].pro) zig(p);
	}
}	
inline void delet(int &p,int data) 
{
	int l=t[p].l,r=t[p].r;
	if(data==t[p].key){
		if(t[p].weight>1){
			t[p].weight--;
			t[p].siz--;
			return;
		}
		if(t[p].l*t[p].r==0) p=t[p].l+t[p].r;
		else if(t[l].pro<t[r].pro){
			zag(p);
			delet(p,data);
		}
		else{
			zig(p);
			delet(p,data);
		}
		return;
	}
	
	t[p].siz--;
	if(t[p].key>data) delet(t[p].l,data);
	else delet(t[p].r,data);
}
/*——————operation——————*/ 
inline void askrank(int p,int data)
{
	if(!p) return;
	int l=t[p].l,r=t[p].r;
	if(data==t[p].key){ans+=t[l].siz;return;}//+=!! 
	if(data<t[p].key) {askrank(l,data);return;}
	ans=ans+t[l].siz+t[p].weight;
	askrank(r,data); 
}

inline void pre(int p,int data)
{
	if(!p) return;
	int l=t[p].l,r=t[p].r;
	if(t[p].key>=data) pre(l,data);
	else{
		ans=max(ans,t[p].key);
		pre(r,data);
	}
}
inline void next(int p,int data)
{
	if(!p) return;
	int l=t[p].l,r=t[p].r;
	if(t[p].key<=data) next(r,data);
	else{
		ans=min(ans,t[p].key);
		pre(l,data);
	}
}
/*——————Segment tree——————*/
#define lson k<<1,l,mid
#define rson k<<1|1,mid+1,r
inline void build(int k,int l,int r,int pos,int data)
{
		insert(rt[k],data);//rt[k]没有?新建一个! 
		if(l==r) return;
		int mid=l+((r-l)>>1);//
		if(pos<=mid) build(lson,pos,data);
		else build(rson,pos,data);
}
inline void modify(int k,int l,int r,int pos,int data,int c)
{
	delet(rt[k],data);///!
	insert(rt[k],c);
	if(l==r) return;
	int mid=l+((r-l)>>1);
	if(pos<=mid) modify(lson,pos,data,c);
	else modify(rson,pos,data,c);
}
inline void getrank(int k,int l,int r,int lx,int rx,int x)
{
	if(lx<=l && r<=rx){
		askrank(rt[k],x);
		return;
	}
	if(l==r) return;
	int mid=l+((r-l)>>1);
	if(lx<=mid) getrank(lson,lx,rx,x);
	if(rx>mid) getrank(rson,lx,rx,x);
}
inline void getnum(int l,int r,int rank)
{//二分排名为k的数(数值),check其是否排名为k 
	int lx=0,rx=1e8+1;
	while(lx<=rx){//左闭右开!!
		int mid=lx+((rx-lx)>>1);
		getrank(1,1,n,l,r,m);
		if(ans<=rank){
			l=mid+1;
			num=m;
		} 
		else rx=mid-1;
	} 
} 
inline void getpre(int k,int l,int r,int lx,int rx,int x)
{
	if(lx<=l && r<=rx) {pre(rt[k],x);return;}
	if(l==r) return;
	int mid=l+((r-l)>>1);
	if(lx<=mid) getpre(lson,lx,rx,x);
	if(rx>mid) getpre(rson,lx,rx,x);
}
inline void getnext(int k,int l,int r,int lx,int rx,int x)
{
	if(lx<=l && r<=rx) {next(rt[k],x);return;}
	if(l==r) return;
	int mid=l+((r-l)>>1);
	if(lx<=mid) getnext(lson,lx,rx,x);
	if(rx>mid) getnext(rson,lx,rx,x);
}

inline void Print(int x){printf("%d\n",x);}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("bzoj3196.txt", "r", stdin);
	#endif
		srand((unsigned)time(NULL));
		n=getint();m=getint();
		F(i,1,n) x[i]=getint();
		F(i,1,n) build(1,1,n,i,x[i]);
	while(m--){
		q=getint();
		int l,r,k,pos,p;
		switch(q){
			case 1://查询数值k在区间内的排名
				l=getint();r=getint();k=getint();
				ans=1;
				getrank(1,1,n,l,r,k);
				Print(ans);
				break; 
			case 2://查询区间内排名为k的值
				l=getint();r=getint();k=getint(); 
				getnum(l,r,k);
				Print(num);
				break;
			case 3://修改某一位置pos上的数值->k
				pos=getint();k=getint();
				p=x[pos];
				x[pos]=k;
				modify(1,1,n,pos,p,k);//!!!!
				break;				
			case 4://查询数值k在区间内的前驱
				l=getint();r=getint();k=getint();
				ans=0;
				getpre(1,1,n,l,r,k);
				Print(ans);
				break;
			case 5://5.查询k在区间内的后继
				l=getint();r=getint();k=getint();
				ans=INF;//!
				getnext(1,1,n,l,r,k);
				Print(ans);
				break;
		}
	}
		return 0;
}

跟自己说声:加油,加油!

Category: 树套树 | Tags: 平衡树 树套树 二分 线段树 C++ | Read Count: 357

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com