线段树专题—POJ 3667 Hotel(区间合并模板)

题意:给一个n和m,表示n个房间,m次操作,操作类型有2种,一种把求连续未租出的房间数有d个的最小的最左边的房间号,另一个操作时把从x到x+d-1的房间号收回。

分析:这是一个区间合并的典型应用,基本只要套套模板就好了

下面是对区间合并的解释:

up函数:当修改完此时的区间时,为了维护该区间上面区间值的正确性,向上回朔更新上面区间的值

down函数:把延迟标记下传,并修改下面区间的值,维护下面区间的正确性区间

(需要注意的是,up函数只有需要更新的时候才需要用到,而down只要是带延迟标记操作时即使只是遍历也是要加上的

update函数:在两种操作结束后对线段树的值维护

query函数:求最小的满足条件的房间号

此外对于此题的一些解释:

lsum数组表示该段rt区间的左连续区间的最大值

rsum数组表示该段rt区间的右连续区间的最大值

msum数组表示该段区间的连续区间的最大值,可以是左连续、右连续、也可以使中间连续

lazy数组表示懒惰标记,-1时表示未打上懒惰标记,不需要改变,0时表示该区间打上了区间被使用,1表示该区间被回收

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include<vector>
#pragma comment(linker,"/STACK:1024000000,1024000000")
using namespace std;
const int maxn = 5e4+5;
int msum[maxn<<2],lsum[maxn<<2],rsum[maxn<<2];
int lazy[maxn<<2];
void up(int l,int r,int rt){
    int mid=(l+r)>>1;
    lsum[rt]=lsum[rt<<1];
    rsum[rt]=rsum[rt<<1|1];
    if(lsum[rt<<1]==mid-l+1) lsum[rt]+=lsum[rt<<1|1];
    if(rsum[rt<<1|1]==r-mid) rsum[rt]+=rsum[rt<<1];
    msum[rt]=max(rsum[rt<<1]+lsum[rt<<1|1],max(msum[rt<<1],msum[rt<<1|1]));
}
void down(int l,int r,int rt){
    if(lazy[rt]!=-1){
        int mid=(l+r)>>1;
        lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
        msum[rt<<1]=lsum[rt<<1]=rsum[rt<<1]=lazy[rt]?mid-l+1:0;
        msum[rt<<1|1]=lsum[rt<<1|1]=rsum[rt<<1|1]=lazy[rt]?r-mid:0;
        lazy[rt]=-1;
    }
}
void build(int l,int r,int rt){
    lazy[rt]=-1;
    msum[rt]=lsum[rt]=rsum[rt]=r-l+1;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}
void update(int L,int R,int k,int l,int r,int rt){
    if(L<=l&&r<=R){
        lazy[rt]=k;
        msum[rt]=lsum[rt]=rsum[rt]=k?r-l+1:0;
        return;
    }
    down(l,r,rt);
    int mid=(l+r)>>1;
    if(L<=mid) update(L,R,k,l,mid,rt<<1);
    if(R>mid)update(L,R,k,mid+1,r,rt<<1|1);
    up(l,r,rt);
}
int query(int len,int l,int r,int rt){
    if(l==r) return l;
    down(l,r,rt);
    int mid=(l+r)>>1;
    if(msum[rt<<1]>=len) return query(len,l,mid,rt<<1);
    else if(rsum[rt<<1]+lsum[rt<<1|1]>=len) return mid-rsum[rt<<1]+1;
    else return query(len,mid+1,r,rt<<1|1);
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    build(1,n,1);
    while(m--){
        int k,x,d;
        scanf("%d",&k);
        if(k==1){
            scanf("%d",&d);
            if(d>msum[1]) printf("0\n");
            else{
                int L=query(d,1,n,1);
                printf("%d\n",L);
                update(L,L+d-1,0,1,n,1);
            }
        }
        else {
            scanf("%d%d",&x,&d);
            update(x,x+d-1,1,1,n,1);
        }
    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-02 16:18:38

线段树专题—POJ 3667 Hotel(区间合并模板)的相关文章

HDU 1540 Tunnel Warfare 平衡树 / 线段树:单点更新,区间合并

Tunnel Warfare                                  Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast

HDU 1540 Tunnel Warfare 线段树:单点更新,区间合并

Tunnel Warfare                                  Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast

POJ 3667 splay区间合并练习

Hotel Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12446   Accepted: 5363 Description 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

线段树(区间合并) POJ 3667 Hotel

题目传送门 1 /* 2 题意:输入 1 a:询问是不是有连续长度为a的空房间,有的话住进最左边 3 输入 2 a b:将[a,a+b-1]的房间清空 4 线段树(区间合并):lsum[]统计从左端点起最长连续空房间数,rsum[]类似,sum[]统计区间最长连续的空房间数, 5 它有三种情况:1.纯粹是左端点起的房间数:2.纯粹是右端点的房间数:3.当从左(右)房间起都连续时,加上另一个子节点 6 从左(右)房间起的数,sum[]再求最大值更新维护.理解没错,表达能力不足 7 详细解释:htt

POJ 3667 Hotel (初遇线段树区间合并)

题意: 有一个线段,从1到n,下面m个操作,操作分两个类型,以1开头的是查询操作,以2开头的是更新操作 1 w 表示在总区间内查询一个长度为w的可用区间并且要最靠左,能找到的话返回这个区间的左端点并占用了这个区间,找不到返回0 2 a len , 表示从单位a开始,清除一段长度为len的区间(将其变为可用,不被占用),不需要输出. 思路: 这是第一次遇到线段树区间合并的题目,写下感悟,还是对线段的更新和查询工作,但是查询的对象的性质已经不像单点那样,查询的是某个线段的最大可用区间是多少,还要一并

POJ 3667 Hotel(线段树)

POJ 3667 Hotel 题目链接 题意:有n个房间,现在有两个操作 1.找到连续长度a的空房间,入住,要尽量靠左边,如果有输出最左边的房间标号,如果没有输出0 2.清空[a, a + b - 1]的房间 思路:线段树的区间合并,记录下左边连续最长和右边连续最长空房间,和每一段的最大值,这样pushup的时候就是进行区间合并,注意查询的时候由于是要尽量左,所以先查左孩子,在查横跨左右的,在查右孩子 代码: #include <cstdio> #include <cstring>

线段树+离散化 POJ 2528 Mayor&#39;s posters

题目传送门 题意:在一面墙上贴海报,有先后顺序,问最后有多少张不同的海报(指的是没被覆盖或者只是部分覆盖的海报) 分析:这题数据范围很大,直接搞超时+超内存,需要离散化:离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只需要1000,1990,2000,2012就够了,将其分别映射到0,1,2,3,在于复杂度就大大的降下来

vj线段树专题

vj线段树专题题解 单点更新模板 void build(int x,int l,int r){//sum[x]控制l-r区域 if(l==r){Sum[x]=num[l];return ;} int mid=l+((r-l)>>1); build(x<<1,l,mid); build(x<<1|1,mid+1,r); Sum[x]=Sum[x<<1]+Sum[x<<1|1]; } void add(int a,int b,int l,int r,

[kuangbin]带你飞之&#39;线段树&#39;专题(未完成)

// 带飞网址 https://vjudge.net/article/187 专题七 线段树 HDU 1166 敌兵布阵HDU 1754 I Hate It√ POJ 3468 A Simple Problem with IntegersPOJ 2528 Mayor's postersHDU 1698 Just a HookZOJ 1610 Count the ColorsPOJ 3264 Balanced LineupHDU 4027 Can you answer these queries?