线段树 (区间合并)【p2894】[USACO08FEB]酒店Hotel
Descripion
奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光。作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿。这个巨大的旅馆一共有N (1 <= N <= 50,000)间客房,它们在同一层楼中顺次一字排开,在任何一个房间里,只需要拉开窗帘,就能见到波光粼粼的湖面。
贝茜一行,以及其他慕名而来的旅游者,都是一批批地来到旅馆的服务台,希望能订到D_i (1 <= D_i <= N)间连续的房间。服务台的接待工作也很简单:如果存在r满足编号为r..r+D_i-1的房间均空着,他就将这一批顾客安排到这些房间入住;如果没有满足条件的r,他会道歉说没有足够的空房间,请顾客们另找一家宾馆。如果有多个满足条件的r,服务员会选择其中最小的一个。
旅馆中的退房服务也是批量进行的。每一个退房请求由2个数字X_i、D_i描述,表示编号为X_i..X_i+D_i-1 (1 <= X_i <= N-D_i+1)房间中的客人全部离开。退房前,请求退掉的房间中的一些,甚至是所有,可能本来就无人入住。
而你的工作,就是写一个程序,帮服务员为旅客安排房间。你的程序一共需要处理M (1 <= M < 50,000)个按输入次序到来的住店或退房的请求。第一个请求到来前,旅店中所有房间都是空闲的。
表示这题很坑爹.
但是的确是一道线段树好题.
线段树需要维护的东西是这些
struct cod{ int l;//左起最长连续空房长度 int r;//右起最长连续空房长度 int len;//区间长度. int tg;//lazy标记 int sum;//区间最长连续空房长度 }tr[N<<4];
知道维护的东西了,现在我们就考虑如何更新,如何下放标记.
PS:这里以\(1\)表示有人住房.\(2\)表示退房
下放操作是这样的.//应该不难理解.
inline void down(int o) { if(!tr[o].tg)return; tr[ls].tg=tr[rs].tg=tr[o].tg;//传递lazy标记 tr[ls].l=tr[ls].r=tr[ls].sum= (tr[o].tg==1 ? 0:tr[ls].len);//判断情况 tr[rs].l=tr[rs].r=tr[rs].sum= (tr[o].tg==1 ? 0:tr[rs].len);//判断情况 tr[o].tg=0;//清除lazy标记 }
然后考虑向上更新.
我们需要维护\(tr[o].l\)和\(tr[o].r\),还有\(tr[o].sum\)
维护\(tr[o].l\),需要考虑左子区间是否全部是空房.()
如果全部是,那么我们的
\(tr[o].l=tr[ls].sum+tr[rs].l\)
否则就是\(tr[o].l=tr[ls].l\)
这就类似于区间合并.//不是很难理解吧.
维护\(tr[o].r\)类似于维护\(tr[o].l\).
维护\(tr[o].sum\)就有三种情况,
- 要么是\(tr[ls].sum\)
- 要么是\(tr[rs].sum\)
- 要么是\(tr[ls].r+tr[rs].l\)
取\(max\)即可
inline void up(int o) { if(tr[ls].sum==tr[ls].len) tr[o].l=tr[ls].sum+tr[rs].l; else tr[o].l=tr[ls].l; if(tr[rs].sum==tr[rs].len) tr[o].r=tr[rs].sum+tr[ls].r; else tr[o].r=tr[rs].r; tr[o].sum=max(max(tr[ls].sum,tr[rs].sum),tr[ls].r+tr[rs].l); }
其他操作不是很难理解,就不过多解释了.
代码
#include<cstdio> #include<cctype> #include<iostream> #define ls o<<1 #define rs o<<1|1 #define N 50008 #define R register using namespace std; inline void in(int &x) { int f=1;x=0;char s=getchar(); while(!isdigit(s)){if(s=='-')f=-1;s=getchar();} while(isdigit(s)){x=x*10+s-'0';s=getchar();} x*=f; } struct cod{ int l; int r; int len; int tg; int sum; }tr[N<<4]; void build(int o,int l,int r) { tr[o].l=tr[o].r=tr[o].len=tr[o].sum=(r-l+1); if(l==r)return; int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); } inline void down(int o) { if(!tr[o].tg)return; tr[ls].tg=tr[rs].tg=tr[o].tg; tr[ls].l=tr[ls].r=tr[ls].sum= (tr[o].tg==1 ? 0:tr[ls].len); tr[rs].l=tr[rs].r=tr[rs].sum= (tr[o].tg==1 ? 0:tr[rs].len); tr[o].tg=0; } inline void up(int o) { if(tr[ls].sum==tr[ls].len) tr[o].l=tr[ls].sum+tr[rs].l; else tr[o].l=tr[ls].l; if(tr[rs].sum==tr[rs].len) tr[o].r=tr[rs].sum+tr[ls].r; else tr[o].r=tr[rs].r; tr[o].sum=max(max(tr[ls].sum,tr[rs].sum),tr[ls].r+tr[rs].l); } void change(int o,int l,int r,int x,int y,int del) { down(o); if(x<=l and y>=r) { tr[o].tg=del; if(del==1) tr[o].l=tr[o].r=tr[o].sum=0; else tr[o].l=tr[o].r=tr[o].sum=tr[o].len; return; } int mid=(l+r)>>1; if(x<=mid)change(ls,l,mid,x,y,del); if(y>mid)change(rs,mid+1,r,x,y,del); up(o); } int query(int o,int l,int r,int x) { down(o); if(l==r)return l; int mid=(l+r)>>1; if(tr[ls].sum>=x) return query(ls,l,mid,x); if(tr[ls].r+tr[rs].l>=x) return mid-tr[ls].r+1; return query(rs,mid+1,r,x); } int n,m; int main() { in(n),in(m);build(1,1,n); for(R int i=1,opt,x,y;i<=m;i++) { in(opt); if(opt==1) { in(x); if(tr[1].sum<x) { puts("0"); continue; } int pos=query(1,1,n,x); printf("%d\n",pos); change(1,1,n,pos,pos+x-1,1); } else { in(x),in(y); change(1,1,n,x,x+y-1,2); } } }
- 洛谷 P2894 [USACO08FEB]酒店Hotel(线段树区间合并)
- 线段树 洛谷P2894 [USACO08FEB]酒店Hotel
- 线段树【洛谷P2894】 [USACO08FEB]酒店Hotel
- POJ - 3667 - Hotel (线段树 - 区间合并)
- POJ 3667 Hotel(线段树区间合并查询)
- Hotel ----线段树并查集,区间合并
- P2894 [USACO08FEB]酒店Hotel
- 【线段树】POJ 3667 Hotel 区间合并
- 洛谷P2894 [USACO08FEB]酒店Hotel
- Poj 3667 - Hotel 线段树-- 区间更新 区间合并 区间查询
- poj3667-Hotel 线段树区间合并
- POJ 3667 Hotel (线段树区间合并)
- POJ 3667 Hotel (线段树区间合并)
- 【线段树】POJ 3667 Hotel 区间合并
- poj 3667 Hotel(线段树,成段更新,区间合并,Lazy思想)
- poj 3667 Hotel(线段树中级,区间合并)
- POJ 3667 & 1823 Hotel (线段树区间合并)
- POJ 3667 Hotel 线段树 区间合并
- POJ 3667 Hotel 线段树 区间合并 入门题
- POJ 3667 Hotel 带区间合并操作的线段树