poj3667(bzoj1593)--Usaco08Feb Hotel--线段树区间合并
Description
奶牛们最近的旅游计划,是到苏必利尔湖畔,享受那里的湖光山色,以及明媚的阳光。作为整个旅游的策划者和负责人,贝茜选择在湖边的一家著名的旅馆住宿。这个巨大的旅馆一共有N (1 <= N <= 50,000)间客房,它们在同一层楼中顺次一字排开,在任何一个房间里,只需要拉开窗帘,就能见到波光粼粼的湖面。 贝茜一行,以及其他慕名而来的旅游者,都是一批批地来到旅馆的服务台,希望能订到Di (1 <= Di <= N)间连续的房间。服务台的接待工作也很简单:如果存在r满足编号为r..r+Di -1的房间均空着,他就将这一批顾客安排到这些房间入住;如果没有满足条件的r,他会道歉说没有足够的空房间,请顾客们另找一家宾馆。如果有多个满足条件的r,服务员会选择其中最小的一个。 旅馆中的退房服务也是批量进行的。每一个退房请求由2个数字Xi、Di 描述,表示编号为Xi..Xi+Di -1 (1 <= Xi <= N-Di +1)房间中的客人全部离开。退房前,请求退掉的房间中的一些,甚至是所有,可能本来就无人入住。 而你的工作,就是写一个程序,帮服务员为旅客安排房间。你的程序一共需要处理M (1 <= M < 50,000)个按输入次序到来的住店或退房的请求。第一个请求到来前,旅店中所有房间都是空闲的。
Input
* 第1行: 2个用空格隔开的整数:N、M
* 第2..M+1行: 第i+1描述了第i个请求,如果它是一个订房请求,则用2个数字 1、Di描述,数字间用空格隔开;如果它是一个退房请求,用3 个以空格隔开的数字2、Xi、Di描述
Output
* 第1..??行: 对于每个订房请求,输出1个独占1行的数字:如果请求能被满足 ,输出满足条件的最小的r;如果请求无法被满足,输出0
Sample Input
10 6
1 3
1 3
1 3
1 3
2 5 5
1 6
1 3
1 3
1 3
1 3
2 5 5
1 6
Sample Output
1
4
7
0
5
4
7
0
5
题解:
感觉思想上和小白逛公园有一点像。
此问题抽象化为:
1.对于一个序列,求是否有连续的一段长为Di的区间的值都为0。如果有,输出最小的左端点编号;如果没有,输出0;
2.清空一段区间的值。
这题维护的信息是:
l_len----从区间左端点开始的最长0子串长度
r_len----从区间右端点开始的最长0子串长度
t_len----整个区间内最长0子串长度
lzy----当前房间状态
合并都写注释里了。
View Code
1 #include<cmath> 2 #include<algorithm> 3 #include<cstdio> 4 #include<iostream> 5 #include<cstring> 6 using namespace std; 7 const int maxn=50009; 8 9 struct tree 10 { 11 int lzy,l,r; 12 int l_len,r_len,t_len; 13 int get_len() 14 { 15 return r-l+1; 16 } 17 int get_mid() 18 { 19 return (l+r)>>1; 20 } 21 void new_len() 22 { 23 t_len=r_len=l_len=(lzy?0:get_len()); 24 } 25 }tr[maxn<<2]; 26 int n,m,d[maxn]; 27 28 void build(int x,int la,int ra) 29 { 30 tr[x].l=la;tr[x].r=ra; 31 tr[x].t_len=tr[x].r_len=tr[x].l_len=tr[x].get_len(); 32 tr[x].lzy=0;//初始时旅馆没有客人 33 if(la==ra)return; 34 int mid=tr[x].get_mid(); 35 build(x<<1,la,mid); 36 build(x<<1|1,mid+1,ra); 37 return; 38 } 39 40 int ask(int tot,int x) 41 { 42 if(tr[x].l==tr[x].r&&tot==1) 43 return tr[x].l;//只需要一间的时候 44 if(tr[x].lzy!=-1) 45 { 46 tr[x<<1].lzy=tr[x<<1|1].lzy=tr[x].lzy; 47 tr[x].lzy=-1; 48 tr[x<<1].new_len();tr[x<<1|1].new_len(); 49 } 50 if(tr[x<<1].t_len>=tot)//在左区间找 51 return ask(tot,x<<1); 52 else if(tr[x<<1].r_len+tr[x<<1|1].l_len>=tot)//中间拼起来的区间刚好覆盖 53 return tr[x<<1].r-tr[x<<1].r_len+1;//直接返回此区间的左端点 54 else if(tr[x<<1|1].t_len>=tot)//在右区间里找 55 return ask(tot,x<<1|1); 56 else 57 return 0;//没有空房间啦qwq 58 } 59 60 void update(int x,int w,int la,int ra) 61 { 62 if(tr[x].l==la&&ra==tr[x].r) 63 { 64 tr[x].lzy=w; 65 tr[x].new_len(); 66 return; 67 } 68 if(tr[x].lzy!=-1) 69 {//下传lazy标记 70 tr[x<<1].lzy=tr[x<<1|1].lzy=tr[x].lzy; 71 tr[x].lzy=-1; 72 tr[x<<1].new_len();//更新覆盖情况 73 tr[x<<1|1].new_len(); 74 } 75 int mid=tr[x].get_mid(); 76 if(la>mid) 77 update(x<<1|1,w,la,ra); 78 else if(ra<=mid) 79 update(x<<1,w,la,ra); 80 else 81 { 82 update(x<<1,w,la,mid); 83 update(x<<1|1,w,mid+1,ra); 84 } 85 86 int tmp=max(tr[x<<1].t_len,tr[x<<1|1].t_len); 87 tr[x].t_len=max(tmp,tr[x<<1].r_len+tr[x<<1|1].l_len); 88 tr[x].l_len=tr[x<<1].l_len; 89 tr[x].r_len=tr[x<<1|1].r_len; 90 91 if(tr[x<<1].t_len==tr[x<<1].get_len()) 92 tr[x].l_len+=tr[x<<1|1].l_len; 93 94 if(tr[x<<1|1].t_len==tr[x<<1|1].get_len()) 95 tr[x].r_len+=tr[x<<1].r_len; 96 return; 97 } 98 99 int main() 100 { 101 scanf("%d%d",&n,&m); 102 build(1,1,n); 103 while(m--) 104 { 105 int op; 106 scanf("%d",&op); 107 if(op==1) 108 { 109 int tot; 110 scanf("%d",&tot); 111 int ans=ask(tot,1); 112 cout<<ans<<endl;//从头开始找 113 if(ans) 114 update(1,1,ans,ans+tot-1);//标记已经住进去 115 } 116 else if(op==2) 117 { 118 int la,len; 119 scanf("%d%d",&la,&len); 120 update(1,0,la,la+len-1);//清空房间 121 } 122 } 123 return 0; 124 }