[P2894][USACO08FEB] 酒店Hotel (线段树+懒标记下传)

题意:有 n个房间,题目给出两个操作,若 op==1,就输出最靠左的连续空房数量为 x的房间序列的最左边的序号,然后将这些房间改为入住;若 op==2,就将从 x~y的的序列全部改为空房;

解法:线段树+懒标记下传;

1.线段树;题目让在一个很长的序列操作很多次,暴力显然过不了,要用线段树优化;

2.懒标记下传;这是本题的精髓。   此题要维护的不是序列上的数值总和,而是该序列的最长连续空房的长度 sum,从该节点从左向右数最长的连续空房长度 lm,以及从该节点从右向左数最长的连续空房长度 rm;同时还要注意在合并区间时的操作;( tag==1表示要将序列变为空房; tag==2表示要将序列变为有房)

附上代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define mid ((l+r)>>1)
#define ls (o*2)
#define rs (o*2+1)
using namespace std;
const int N = 5e4+10;

inline int read(){
    int ref=0,x=1;char ch=getchar();
    while(!isdigit(ch)){if(ch==‘-‘)x=-1;ch=getchar();}
    while(isdigit(ch)){ref=ref*10+ch-‘0‘;ch=getchar();}
    return ref*x;
}

int n,m;
struct tree{
    int sum,lm,rm,len;
}a[N<<4];
int tag[N<<4];

void pushup(int o){
    a[o].lm=(a[ls].lm==a[ls].len)?a[ls].len+a[rs].lm:a[ls].lm;
    a[o].rm=(a[rs].rm==a[rs].len)?a[rs].len+a[ls].rm:a[rs].rm;
    a[o].sum=max(max(a[ls].sum,a[rs].sum),a[ls].rm+a[rs].lm);
}

void pushdown(int o){
    if(!tag[o]) return ;
    tag[ls]=tag[rs]=tag[o];
    a[ls].sum=a[ls].lm=a[ls].rm=(tag[o]==1)?a[ls].len:0;
    a[rs].sum=a[rs].lm=a[rs].rm=(tag[o]==1)?a[rs].len:0;
    tag[o]=0;
}

void build(int o,int l,int r){
    a[o].len=a[o].lm=a[o].rm=a[o].sum=r-l+1;
    if(l==r) return ;
    build(ls,l,mid);
    build(rs,mid+1,r);
}

void updata(int o,int l,int r,int x,int y,int lz){
    if(x<=l&&y>=r){
        a[o].sum=a[o].lm=a[o].rm=(lz==1)?a[o].len:0;
        tag[o]=lz;
        return ;
    }
    pushdown(o);
    if(x<=mid) updata(ls,l,mid,x,y,lz);
    if(y>mid) updata(rs,mid+1,r,x,y,lz);
    pushup(o);
}

int query(int o,int l,int r,int lenth){
    pushdown(o);
    if(l==r) return l;
    if(a[ls].sum>=lenth) return query(ls,l,mid,lenth);
    if(a[ls].rm+a[rs].lm>=lenth) return mid-a[ls].rm+1;
    else return query(rs,mid+1,r,lenth);
}

int main()
{
    scanf("%d%d",&n,&m);
    build(1,1,n);
    for(int i=1;i<=m;i++){
        int x,y,op;
        op=read();
        if(op==1){
            x=read();
            if(a[1].sum>=x){
                int ans=query(1,1,n,x);
                printf("%d\n",ans);
                updata(1,1,n,ans,ans+x-1,2);
            }
            else printf("0\n");
        }
        else {
            x=read(),y=read();
            updata(1,1,n,x,x+y-1,1);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/nnezgy/p/11396850.html

时间: 2024-10-11 23:38:39

[P2894][USACO08FEB] 酒店Hotel (线段树+懒标记下传)的相关文章

线段树(标记下传乘法和加法)

传送门:https://www.luogu.org/problem/P3373 标记下传,这种东西就是在求和和更改的时候进行pushdown把标记(各种标记,类似于寒冰标记.痛苦标记.穹妹标记……)下传,来节省时间. 还是挺简单的,主要问题处在pushdown上但多看看就会了 #include<bits/stdc++.h> using namespace std; long long n,m,p; long long multag[1000009]; long long addtag[1000

P2894 [USACO08FEB]酒店Hotel

P2894 [USACO08FEB]酒店Hotel 题目描述 The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on fam

浅谈线段树 (例题:[USACO08FEB]酒店Hotel)By cellur925

今天我们说说线段树. 我个人还是非常欣赏这种数据结构的.(逃)因为它足够优美,有递归结构,有左子树和右子树,还有二分的思想. emm这个文章打算自用,就不写那些基本的操作了... 1° 简单的懒标记(仅含加法) 当我们进行区间修改(比如同时加上一个数)时,我们现在也许暂时不用它,可以当需要用的时候再改.这个时候我们就需要做个标记,这个标记就是懒标记,$lazy$.如果在后续的指令中需要从p向下递归,我们这时候检查它是否有标记.若有,就按照标记更新两个子节点,同时为子节点增加标记,清除p的标记.

线段树lazy标记??Hdu4902

Nice boat Time Limit: 30000/15000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 335    Accepted Submission(s): 159 Problem Description There is an old country and the king fell in love with a devil. The devil al

fzu 2171 线段树 lazy标记

http://acm.fzu.edu.cn/problem.php?pid=2171      Problem 2171 防守阵地 II Accept: 73    Submit: 256Time Limit: 3000 mSec    Memory Limit : 32768 KB Problem Description 部队中总共有N个士兵,每个士兵有各自的能力指数Xi,在一次演练中,指挥部确定了M个需要防守的地点,指挥部将选择M个士兵依次进入指定地点进行防守任务,获得的参考指数即为M个士兵

POJ 3468 线段树+lazy标记

lazy标记 Time Limit:5000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u Submit Status Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to

HDU 1698 Just a Hook (线段树延迟标记(lazy))

题意:有n个数初始值都为1,m个操作a,b,c,表示把区间[a,b]变为c,求最后n个数的和. 经典区间更新求和问题,需要用到延迟标记(或者说是懒惰标记),简单老说就是每次更新 的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新或询问的时候. #include<cstdio> #include<stdlib.h> #include<string.h> #include<string> #include<map> #include<cm

hdu-3397 Sequence operation 线段树多种标记

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3397 题目大意: 0 a b表示a-b区间置为0 1 a b表示a-b区间置为1 2 a b表示a-b区间中的0变成1,1变成0 3 a b表示a-b区间中的1的数目 4 a b表示a-b区间中最长连续1的长度 解题思路: 线段树多种标记. 需要处理的东西比较多: 做题的时候发现一个问题: 我的宏定义Max不可以用于函数,尤其是递归函数,这样会使得同一函数重复调用好几遍,递归函数的话更会超时. 1

poj 3667 Hotel - 线段树

The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on famed Cumberland Street as their v