Poj 3667 Hotel(线段树)

题目链接
思路

用线段树维护三个最值

ms:在区间中连续最大的空房间数量
ls::在区间中左边连续的最大空房间的数量
rs:在区间中右边连续的嘴阀空房间的数量

  • query
    在询问的过程中,优先判断左儿子的空房间数量ms是否满足条件,这样能保证如果存在答案,答案总是最靠左的
  • update
    基本和带修的线段树的update函数一样
代码
//#pragma GCC optimize(2)
//#include<bits/stdc++.h>
#include<cstdio>
#include<iostream>


using namespace std;

typedef long long ll;
typedef unsigned long ul;
typedef unsigned long long ull;
#define pi acos(-1.0)
#define e exp(1.0)
#define pb push_back
#define mk make_pair
#define fir first
#define sec second
#define scf scanf
#define prf printf
typedef pair<ll,ll> pa;
const int dir_4[4][2]={-1,0,0,1,1,0,0,-1};
const int dir_8[8][2]={-1,-1,-1,0,-1,1,0,1,1,1,1,0,1,-1,0,-1};
const ll INF=0x3f3f3f3f3f3f3f3f;
const int inf=0x3f3f3f3f;
const int maxn=5e5+7;//RE的话,尽量开大点 
int N,M,lazy[maxn];//lazy:-1(没有标记),0(空房间),1(房间已用)
struct Node{
	int l,r,ms,ls,rs;
	Node(){}
	Node(int l,int r,int ms,int ls,int rs):l(l),r(r),ms(ms),ls(ls),rs(rs){}
	int len(){
		return r-l+1;
	}
	int mid(){
		return (l+r)>>1;
	}
}node[maxn<<2];
void built(int l,int r,int rt){
	int len=r-l+1;
	node[rt]=Node(l,r,len,len,len);
	lazy[rt]=-1;
	if(l==r)
	return ;
	int mid=(l+r)>>1;
	built(l,mid,rt<<1);
	built(mid+1,r,rt<<1|1); 
} 
void up(int rt){
	node[rt].ls=node[rt<<1].ls;
	node[rt].rs=node[rt<<1|1].rs;
	if(node[rt<<1].ls==node[rt<<1].len())
	node[rt].ls+=node[rt<<1|1].ls;
	if(node[rt<<1|1].rs==node[rt<<1|1].len())
	node[rt].rs+=node[rt<<1].rs;
	node[rt].ms=max(max(node[rt<<1].ms,node[rt<<1|1].ms),node[rt<<1].rs+node[rt<<1|1].ls);
	return ; 
}
void down(int rt){
	if(lazy[rt]!=-1){
		lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
		if(lazy[rt]==0){
			node[rt<<1].ms=node[rt<<1].ls=node[rt<<1].rs=node[rt<<1].len();
			node[rt<<1|1].ms=node[rt<<1|1].ls=node[rt<<1|1].rs=node[rt<<1|1].len();
		}
		else{
			node[rt<<1].ms=node[rt<<1].ls=node[rt<<1].rs=0;
			node[rt<<1|1].ms=node[rt<<1|1].ls=node[rt<<1|1].rs=0;
		}
		lazy[rt]=-1;
	}
	return ;
}
void update(int l,int r,int va,int rt){
	if(node[rt].l==l&&node[rt].r==r){
		lazy[rt]=va;
		if(va==0)
		node[rt].ms=node[rt].ls=node[rt].rs=node[rt].len();
		else
		node[rt].ms=node[rt].ls=node[rt].rs=0;
		return ;
	}
	if(node[rt].l!=node[rt].r)
	down(rt);
	int m=node[rt].mid();
	if(r<=m)
	update(l,r,va,rt<<1);
	else if(l>m)
	update(l,r,va,rt<<1|1);
	else{
		update(l,m,va,rt<<1);
		update(m+1,r,va,rt<<1|1);
	}
	up(rt);
//	cout<<"wodiu"<<endl;
}
int query(int x,int rt){
	if(node[rt].l==node[rt].r)
	return node[rt].l;
	down(rt);
	int m=node[rt].mid();
	if(node[rt<<1].ms>=x)
	return query(x,rt<<1);
	else if(node[rt<<1].rs+node[rt<<1|1].ls>=x)
	return m-node[rt<<1].rs+1;
	else return query(x,rt<<1|1);
}
int main()
{
//  freopen(".../.txt","w",stdout);
//  freopen(".../.txt","r",stdin);
//	ios::sync_with_stdio(false);
	int i,j,k,op,x,y;
	scf("%d%d",&N,&M);
	built(1,N,1);
	while(M--){
		scf("%d%d",&op,&x);
		if(op==2)
		scf("%d",&y);
		if(op==1){//有顾客租房子 
			if(node[1].ms<x){
				prf("0\n");
			}
			else{
				y=query(x,1);
				prf("%d\n",y);
				update(y,x+y-1,1,1);				
			}
		}
		else //有顾客退房子 
		update(x,x+y-1,0,1); 
	}
	return 0;
}
/*
10 6
1 3
1 3
1 3
1 3
2 5 5
1 6
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值