刷题记录:牛客NC24961Hotel

文章讲述了如何利用线段树数据结构来解决一个关于查询和更新区间内最长连续空房间的问题。通过维护从左端点和右端点开始的最长连续空区间,可以高效地进行查询和退房操作。当需要找到特定长度的连续空房间时,使用二分查找策略在树中定位。代码示例展示了线段树的构建、更新和查询过程。
摘要由CSDN通过智能技术生成

传送门:牛客

题目描述:

参考样例,第一行输入n,m ,n代表有n个房间,编号为1---n,开始都为空房,m表示以下有m行操作,
以下 每行先输入一个数 i ,表示一种操作:
若i为1,表示查询房间,再输入一个数x,表示在1--n 房间中找到长度为x的连续空房,输出连续x个房间
中左端的房间号,尽量让这个房间号最小,若找不到长度为x的连续空房,输出0。若找得到,在这x个空
房间中住上人。
若i为2,表示退房,再输入两个数 x,y 代表 房间号 x---x+y-1 退房,即让房间为空。
输入:
10 6
1 3
1 3
1 3
1 3
2 5 5
1 6
输出:
1
4
7
0
5

首先我们看完题目后肯定会需要维护一个区间最长的连续空间.

而对于维护这一个性质,我们就需要知道一个区间从左端点开始的最长的连续空间以及从右端点开始的最长的连续空间.这样的话在我们进行区间合并的时候才能维护出区间最长的连续空间

那么此时我们的问题就变成了如何找到最早出现的长度为x的连续空区间.我们可以使用线段树的二分性质来做.

int query(int v,int rt) {
	if(tree[rt].l==tree[rt].r) return tree[rt].l;
	if(tree[rt].lazy!=-1) pushdown(rt);
	if(v<=tree[ls].sum) return query(v,ls);
	else if(tree[ls].rmax+tree[rs].lmax>=v) return tree[ls].r-tree[ls].rmax+1;
	else return query(v,rs);
}

现在仔细的讲一下我们的 q u e r y query query部分,我们先不断的判断左子树的最长连续空间是否比x在,如果比x大的话就意味着我们的要找的区间肯定在左子树上,然后当我们发现此时的左子树不够时,此时意味着什么,因为我们上一次是够的,说明此时的区间要么在右子树上,要么是横跨了左右子树(不完全在左子树上).那么这样的话,我们肯定是需要判断后者情况,因为后者的左端点在前者的前面,符合题意找到最前的一个情况


下面是具体的代码部分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
struct Segment_tree{
	int l,r,sum,lazy,lmax,rmax;
}tree[maxn*4];
int n,m;
void pushup(Segment_tree &u,Segment_tree &l,Segment_tree &r) {
	u.sum=max(l.sum,r.sum);
	u.sum=max(u.sum,l.rmax+r.lmax);
	u.lmax=l.lmax;u.rmax=r.rmax;
	if(l.lmax==l.r-l.l+1) u.lmax+=r.lmax;
	if(r.rmax==r.r-r.l+1) u.rmax+=l.rmax;
}
void pushup(int rt) {
	pushup(tree[rt],tree[ls],tree[rs]);
}
void build(int l,int r,int rt) {
	tree[rt].l=l;tree[rt].r=r;tree[rt].lazy=-1;
	if(l==r) {
		tree[rt].sum=tree[rt].lmax=tree[rt].rmax=1;
		return ;
	}
	int mid=(l+r)>>1;
	build(lson);build(rson);
	pushup(rt);
}
void change(int rt,int v) {
	int len=tree[rt].r-tree[rt].l+1;
	tree[rt].sum=tree[rt].lmax=tree[rt].rmax=(v==1?len:0);
	tree[rt].lazy=v;
}
void pushdown(int rt) {
	change(ls,tree[rt].lazy);change(rs,tree[rt].lazy);
	tree[rt].lazy=-1;
}
void update(int l,int r,int rt,int v) {
	if(tree[rt].l==l&&tree[rt].r==r) {
		change(rt,v);
		return ;
	}
	if(tree[rt].lazy!=-1) pushdown(rt);
	int mid=(tree[rt].l+tree[rt].r)>>1;
	if(r<=mid) update(l,r,ls,v);
	else if(l>mid) update(l,r,rs,v);
	else update(l,mid,ls,v),update(mid+1,r,rs,v);
	pushup(rt);
}
int query(int v,int rt) {
	if(tree[rt].l==tree[rt].r) return tree[rt].l;
	if(tree[rt].lazy!=-1) pushdown(rt);
	if(v<=tree[ls].sum) return query(v,ls);
	else if(tree[ls].rmax+tree[rs].lmax>=v) return tree[ls].r-tree[ls].rmax+1;
	else return query(v,rs);
}
int main() {
	n=read();m=read();
	build(root);
	for(int i=1;i<=m;i++) {
		int opt=read();
		if(opt==1) {
			int x=read();
			if(tree[1].sum<x) {
				puts("0");
				continue;
			}
			int pos=query(x,1);
			printf("%d\n",pos);
			update(pos,pos+x-1,1,0);
		}
		else {
			int l=read(),len=read();
			update(l,l+len-1,1,1);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值