poj 3667 线段树前后缀区间 Hotel

本文介绍了一个使用线段树数据结构解决特定空位查询与填充问题的方法。问题涉及对一系列空位进行查询是否有连续空位及对其进行填充或清空的操作。文章详细解析了如何通过线段树维护区间状态,并提供了完整的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题意】

一共有n个空位,初始全为空,会有两种操作,一种是询问是否有r个连续的空位,若有,则将最左端有r个连续的空位的地方全部填满,若没有则不填,另一种是将从u开始的d个位置全部清空

【输入】

第一行两个数n、m(<=50000),表示n个空位,m次操作

接下来m行每行表示一次操作,

若该行第一个数字为1,接下来是一个数字,表示操作一

若该行第一个数字为2,接下来是两个数字,表示操作二

【输出】

对于每个操作一,若存在r个连续的空位,则输出最靠左的r个连续空位的起点,若不存在则输出0

解:

      用lsum表示当前区间最长连续前缀的长度,rsum表示当前区间最长连续后缀的长度,msum表示本个区间最长连续段的长度。重点是还有一个cover,-1表示这个区间既有非空的,又有空的; 1 表示整个当前区间都满了; 0 表示整个区间都空了。 就是lazy。 

     查询的时候,首先,得满足有这样连续的长度。其次:

                            如果当前区间前缀长度就大于等于我们想要的长度的话,就返回左端点。 

                            如果左孩子的最长长度>=wanted的话,就去左孩子里面找。

                            如果包含m的连续区间>=wanted的话,就直接返回左孩子后缀的最左端。

                            不然,就只能去右孩子里面找了。

做题过程:

        开始没有cover,没有push_dn,就只有一个push_up。 想来更新的时候不是没有更新到底吗,也就是说孩子还没更新呢,所以一定得有cover的呀。

        现在再一想,果然表示当前区间是线段树的根本,我怎么给忘了呢。。。

/*
Pro: 0

Sol:

date:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <vector>
#define maxn 50010
#define lson l,m , rt << 1
#define rson m + 1, r , rt << 1 | 1
#define ls rt << 1
#define rs rt << 1 | 1
int n,Q,a,b,op;
using namespace std;
int lsum[maxn << 2], rsum[maxn << 2], msum[maxn << 2], cover[maxn << 2];
void build(int l, int r, int rt){
    lsum[rt] = rsum[rt] = msum[rt] = r - l + 1;
    cover[rt] = 0;//
    if(l == r) return ;
    int m = (l + r) >> 1;
    build(lson); build(rson);
}
void push_dn(int rt, int m){
    if(cover[rt] != -1){
        cover[ls] = cover[rs] = cover[rt];
        lsum[ls] = rsum[ls] = msum[ls] = cover[rt] == 0? m - (m >> 1): 0;//
        lsum[rs] = rsum[rs] = msum[rs] = cover[rt] == 0? (m >> 1): 0;
        cover[rt] = -1;
    }
}
void push_up(int rt,int m){
    lsum[rt] = lsum[ls];
    rsum[rt] = rsum[rs];
    if(lsum[ls] == m - (m >> 1)) lsum[rt] += lsum[rs];
    if(rsum[rs] == m >> 1) rsum[rt] += rsum[ls];
    msum[rt] = max(msum[ls] , msum[rs]);
    msum[rt] = max(msum[rt], rsum[ls] + lsum[rs]);
}
//int query(int dis, int l, int r, int rt){//notOnlySuccess 的版本, 有点不是很懂。。。
//    if(l == r) return l;//
//    push_dn(rt, r - l + 1);
//    int m = (l + r) >> 1;
//    if(msum[ls] >= dis) return query(dis,lson);
//    if(rsum[ls] + lsum[rs] >= dis) return m - rsum[ls] + 1;
//    return query(dis, rson);
//}
int query(int dis, int l, int r, int rt){//寻找长度为dis的最左边的区间的左端点,于是就自己借鉴着改了一改,A了
    if(lsum[rt] >= dis) return l;
    push_dn(rt, r - l + 1);
    int m = (l + r) >> 1;
    if(msum[ls] >= dis) return query(dis,lson);
    if(rsum[ls] + lsum[rs] >= dis) return m - rsum[ls] + 1;
    return query(dis, rson);
}
void update(int L,int R, int sign, int l, int r, int rt){
    if(L <= l && r <= R){
        lsum[rt] = rsum[rt] = msum[rt] = sign == 0 ? r - l + 1 : 0;
        cover[rt] = sign;
        return ;
    }push_dn(rt,r - l + 1);
    int m = (l + r) >> 1;
    if(L <= m) update(L,R,sign,lson);
    if(R > m) update(L,R,sign,rson);
    push_up(rt,r - l + 1);//
}
int main(){
    while(~scanf("%d%d",&n,&Q)){
        build(1,n,1);
        for(int i = 0;  i < Q; i ++){
            scanf("%d",&op);
            if(op == 1){
                scanf("%d",&a);
                if(msum[1] < a) puts("0");
                else{
                    int st = query(a,1,n,1);
                    printf("%d\n", st);
                    update(st, st + a - 1, 1 ,1, n, 1);
                }
            }else{
                scanf("%d%d",&a,&b);
                update(a, a +  b - 1, 0, 1, n, 1);//0 表示没住人
            }
        }
    }
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值