【BZOJ】1593: [Usaco2008 Feb]Hotel 旅馆

【算法】线段树(经典线段树上二分)

【题意】n个房间,m个询问,每次订最前的连续x个的空房间,或退订从x开始y个房间,求每次订的最左房间号。

【题解】关键在于找连续x个空房间,经典二分。

线段树标记sum,lsum,rsum,表示最长连续房间,从左开始最长连续房间,从右开始最长连续房间。

对于区间k,如果k.sum<x,则无解。

否则,如果l(k).sum>=x,则在左区间。

否则,如果l(k).rsum+r(k).lsum>=x,则在中间,那么l(k).r-l(k).rsum+1就是答案。

否则,则在右区间。

这样可以准确的定位,也体现了线段树被称之为区间树的特点,可以将询问分成若干个完整的区间,只要维护区间信息即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=50010;
struct tree{int l,r,lsum,rsum,sum,delta;}t[maxn*4];
int n,m;

void build(int k,int l,int r){
    t[k].l=l;t[k].r=r;t[k].sum=t[k].lsum=t[k].rsum=r-l+1;t[k].delta=-1;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void modify(int k,int x){
    if(x==1){
        t[k].lsum=t[k].rsum=t[k].sum=0;
    }
    else{
        t[k].lsum=t[k].rsum=t[k].sum=t[k].r-t[k].l+1;
    }
}
void update(int k){
    t[k].sum=max(t[k<<1].rsum+t[k<<1|1].lsum,max(t[k<<1].sum,t[k<<1|1].sum));
    t[k].lsum=t[k<<1].lsum;if(t[k<<1].lsum==t[k<<1].r-t[k<<1].l+1)t[k].lsum+=t[k<<1|1].lsum;
    t[k].rsum=t[k<<1|1].rsum;if(t[k<<1|1].rsum==t[k<<1|1].r-t[k<<1|1].l+1)t[k].rsum+=t[k<<1].rsum;
}
void push_down(int k){
    if(~t[k].delta){
        modify(k<<1,t[k].delta);t[k<<1].delta=t[k].delta;
        modify(k<<1|1,t[k].delta);t[k<<1|1].delta=t[k].delta;//传标记
        t[k].delta=-1;
    }
}
int ask(int k,int x){
    push_down(k);
    if(t[k].sum<x)return 0;
    if(t[k<<1].sum>=x)return ask(k<<1,x);
    if(t[k<<1].rsum+t[k<<1|1].lsum>=x)return t[k<<1].r-t[k<<1].rsum+1;
    return ask(k<<1|1,x);
}
void insert(int k,int l,int r,int x){
    if(l<=t[k].l&&t[k].r<=r)t[k].delta=x,modify(k,x);//打标记
    else{
        push_down(k);
        int mid=(t[k].l+t[k].r)>>1;
        if(l<=mid)insert(k<<1,l,r,x);
        if(r>mid)insert(k<<1|1,l,r,x);
        update(k);
    }
}

int main(){
    scanf("%d%d",&n,&m);
    build(1,1,n);
    int p,x,y;
    for(int i=1;i<=m;i++){
        scanf("%d",&p);
        if(p==1){
            scanf("%d",&x);
            printf("%d\n",y=ask(1,x));
            if(y)insert(1,y,y+x-1,1);
        }
        else{
            scanf("%d%d",&x,&y);
            insert(1,x,x+y-1,0);
        }
    }
    return 0;
}

时间: 2024-08-08 05:27:42

【BZOJ】1593: [Usaco2008 Feb]Hotel 旅馆的相关文章

bzoj 1593: [Usaco2008 Feb]Hotel 旅馆

1593: [Usaco2008 Feb]Hotel 旅馆 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 800  Solved: 441[Submit][Status][Discuss] Description 奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光.作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿.这个巨大的旅馆一共有N (1 <= N <= 50,000)间客房,它们在同一层楼中顺次一字排

1593: [Usaco2008 Feb]Hotel 旅馆

1593: [Usaco2008 Feb]Hotel 旅馆 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 489  Solved: 272[Submit][Status][Discuss] Description 奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光.作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿.这个巨大的旅馆一共有N (1 <= N <= 50,000)间客房,它们在同一层楼中顺次一字排

1593: [Usaco2008 Feb]Hotel 旅馆 (线段树)

1593: [Usaco2008 Feb]Hotel 旅馆 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 451  Solved: 262[Submit][Status][Discuss] Description 奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光.作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿.这个巨大的旅馆一共有N (1 <= N <= 50,000)间客房,它们在同一层楼中顺次一字排

bzoj1593 [Usaco2008 Feb]Hotel 旅馆

1593: [Usaco2008 Feb]Hotel 旅馆 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 807  Solved: 447[Submit][Status][Discuss] Description 奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光.作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿.这个巨大的旅馆一共有N (1 <= N <= 50,000)间客房,它们在同一层楼中顺次一字排

【分块】bzoj1593 [Usaco2008 Feb]Hotel 旅馆

分块,记录每个块内包括左端点的最大连续白段的长度, 整个块内的最大连续白段的长度, 和包括右端点的最大连续白段的长度. Because 是区间染色,所以要打标记. 至于怎样在O(sqrt(n))的时间内找到最左的白色段呢?非常恶心…… 要考虑跨块的白段和块内的白段,并且顺序不能反.(代码中高亮的部分) 这题完全体现不出分块编程复杂度低的优势,完全逊色于线段树. So 综上,对于要进行复杂的成段修改的题目,分块的编程复杂度较高,几乎没有优势,不推荐写. 1 #include<cstdio> 2

BZOJ 1592: [Usaco2008 Feb]Making the Grade 路面修整( dp )

最优的做法最后路面的高度一定是原来某一路面的高度. dp(x, t) = min{ dp(x - 1, k) } + | H[x] - h(t) | ( 1 <= k <= t ) 表示前 i 个路面单调不递减, 第 x 个路面修整为原来的第 t 高的高度. 时间复杂度O( n³ ). 令g(x, t) = min{ dp(x, k) } (1 <= k <= t), 则转移O(1), g() 只需在dp过程中O(1)递推即可, 总时间复杂度为O( n² ) 然后单调不递增也跑一遍

BZOJ 1609: [Usaco2008 Feb]Eating Together麻烦的聚餐( LIS )

求LIS , 然后用 n 减去即为answer ---------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep( i , n ) for( int i = 0 ;  i < n ; ++i )

BZOJ 1609: [Usaco2008 Feb]Eating Together

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1609 解:题目很明显,我们只要找到最长不下降子序列,然后总长度减去它的长度就可以了,用o(nlogn)的方法. 但是,用O(9n)的动归,显然更优(吧...) 我学习了一下他人的动归. 用f[i][j](1<=i<=n;j=1,2,3)来表示序列前i个的时候,我们当前用j 这个数字结尾最多需要改几个.当然我们还要枚举上一段的位置,具体我程序里写吧. #include<iostre

BZOJ 1611: [Usaco2008 Feb]Meteor Shower流星雨

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1611 解:直接广搜... 程序: #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<queue> #define maxn 210000000 using namespace std; int ans; int f[500][5