[USACO08FEB]酒店Hotel

传送门
这题可以使用线段树做,每个节点维护三个信息:前缀、后缀和中间的最大值。但是,线段树的题怎么能用线段树做呢,所以我写了一个fhq treap的代码(成功把蓝题变成紫题)

平衡树可以维护数列信息。具体做法是将平衡树的节点以下标为关键字组织,即节点在平衡树内的rank即为其在数列里的位置。这样,每个子树都代表一个区间。

为了输出位置,每个节点还要格外记录横跨其的段的左端点相对于其代表的区间的左端点的偏移量(即crossl数组)。在查询的时候,delta记录该区间的左端点的位置。

要注意pushup和pushdown不能乱。

#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 5e4+5;
int readint(){
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){
		if(s=='-')
			f=-1;
		sc;
	}
	while(s>='0'&&s<='9'){
		x=(x<<3)+(x<<1)+(s^48);
		sc;
	}
	#undef sc
	return x*f;
}
struct zz{
	int l,r,lazy;
	int lm,rm,m;
}t[maxn<<2];
void push(int num,int v){
	if(v==1)
		t[num].lm=t[num].rm=t[num].m=0;
	else
		t[num].lm=t[num].rm=t[num].m=t[num].r-t[num].l+1;
	t[num].lazy=v;
}
void push_up(int num){
	t[num].lm=t[num<<1].lm;
	t[num].rm=t[num<<1|1].rm;
	if(t[num].lm==t[num<<1].r-t[num<<1].l+1)
		t[num].lm+=t[num<<1|1].lm;
	if(t[num].rm==t[num<<1|1].r-t[num<<1|1].l+1)
		t[num].rm+=t[num<<1].rm;
	t[num].m=max(max(t[num<<1].m,t[num<<1|1].m),t[num<<1].rm+t[num<<1|1].lm);
}
void push_down(int num){
	if(t[num].lazy==-1){
		return;
	}
	push(num<<1,t[num].lazy);
	push(num<<1|1,t[num].lazy);
	t[num].lazy=-1;
}
void build(int l,int r,int num){
	t[num].l=l;
	t[num].r=r;
	t[num].lm=t[num].rm=t[num].m=r-l+1;
	if(l==r)
		return ;
	int mid=(l+r)>>1;
	build(l,mid,num<<1);
	build(mid+1,r,num<<1|1);
}
void update(int goal_l,int goal_r,int num,int v){
	if(goal_l<=t[num].l&&goal_r>=t[num].r){
		push(num,v);
		return ;
	}
	push_down(num);
	int mid=(t[num].l+t[num].r)>>1;
	if(goal_r<=mid)
		update(goal_l,goal_r,num<<1,v);
	else if(goal_l>mid)
		update(goal_l,goal_r,num<<1|1,v);
	else{
		update(goal_l,mid,num<<1,v);
		update(mid+1,goal_r,num<<1|1,v);
	}
	push_up(num);
}
int query(int num,int d){
	if(t[num].m<d){
		return 0;
	}
	if(t[num].l==t[num].r)
		return t[num].l;
	push_down(num);
	int mid=(t[num].l+t[num].r)>>1;
	if(t[num<<1].m>=d)
		return query(num<<1,d);
	else if(t[num<<1].rm+t[num<<1|1].lm>=d)
		return mid-t[num<<1].rm+1;
	else if(t[num<<1|1].m>=d)
		return query(num<<1|1,d);
}
int main (){
	//freopen("hotel.in","r",stdin);
	//freopen("hotel.out","w",stdout);
	int n=readint(),m=readint();
	build(1,n,1);
	while(m--){ 
		int t=readint();
		if(t==1){
			int x=readint();
			int ans=query(1,x);
			printf("%d\n",ans);
			if(ans){
				update(ans,ans+x-1,1,1);
			}
		}
		else{
			int x=readint(),d=readint();
			update(x,x+d-1,1,0);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值