[线段树] (h) poj3667 (区间合并)
如在阅读本文时遇到不懂的部分,请在评论区询问,或跳转 线段树总介绍
【题目大意】
有一个旅馆,有N个房间排成一排,现在有两种操作,第一是有X个顾客要入住连续的X个房间,
要求输出最小的左端点的位置,不能满足就输出0,第二是将以L开始,长度为X的连续房间清空。
【输入文件】
第一行两个数N,M,表示房间数和操作数
接下来M行,每行有两种情况:
1 X 表示操作1
2 L X 表示操作2
【输出文件】
对于每一个1操作,输出答案。
题即为求最靠左的连续区间并置满以及把一段区间置空。
那么我们的线段树正式进入了区间合并的部分
0表示没有牛,1表示住了牛
用pre(前驱)表示该区间内的前导零个数,last(后继,用suf比较好但当时写了last不想改了,一个意思),表示后导零个数,Maxilen表示区间内最大连续0的个数
,tag表示该区间内全部置为tag(初值为-1)
pushup注意特殊情况,若 左/右 区间的 前驱/后继 完全覆盖了 左/右 区间,要特殊处理
if(pre[LS]==mid-l+1)pre[rt]=pre[RS]+pre[LS]; else pre[rt]=pre[LS]; if(last[RS]==r-mid)last[rt]=last[RS]+last[LS]; else last[rt]=last[RS];
Maxilen[rt]=max (Maxilen[LS] ,Maxilen[RS] ,pre[RS]+last[LS] );
//最大连续区间可能在 左边/右边/中间(所以为什么维护前驱后继)
pushdown
整段操作,赋0 or 赋1
tag[LS]=tag[RS]=tag[rt]; MaxiLen[LS]=pre[LS]=last[LS]=!tag[rt]?(mid-l+1):0; MaxiLen[RS]=pre[RS]=last[RS]=!tag[rt]?(r-mid):0; tag[rt]=-1;
代码
#include<iostream> #include<cstdio> #include<algorithm> #include<cstdlib> #include<cstring> #include<string> using namespace std; const int N=2e6+3; int pre[N<<2],last[N<<2],MaxiLen[N<<2],tag[N<<2]; int n,m,L; //tag -1 -> Nothing 0 ; 0/1 -> get to 0/1 #define LS (rt<<1) #define RS (LS|1) void pushup(int rt,int l,int r){ MaxiLen[rt]=max(max(MaxiLen[LS],MaxiLen[RS]),pre[RS]+last[LS]); int mid=l+r>>1; if(pre[LS] == mid-l+1)pre[rt]=mid-l+1+pre[RS]; else pre[rt]=pre[LS]; if(last[RS] == r-mid)last[rt]=r-mid+last[LS]; else last[rt]=last[RS]; } void build(int rt,int l,int r){ pre[rt]=last[rt]=MaxiLen[rt]=r-l+1; tag[rt]=-1; if(l==r)return; int mid=l+r>>1; build(LS,l,mid); build(RS,mid+1,r); //pushup(rt,l,r); } void pushdown(int rt,int l,int r){ if(tag[rt] == -1)return; tag[LS]=tag[RS]=tag[rt]; int mid=l+r>>1; MaxiLen[LS]=pre[LS]=last[LS]=!tag[rt]?(mid-l+1):0; MaxiLen[RS]=pre[RS]=last[RS]=!tag[rt]?(r-mid):0; tag[rt]=-1; return; } void update(int rt,int l,int r,int x,int y,int p){ if(l>=x&&r<=y){ tag[rt]=p; //直接覆盖 MaxiLen[rt]=!p?(r-l+1):0; pre[rt]=last[rt]=!p?(r-l+1):0; return; }pushdown(rt,l,r); int mid=l+r>>1; if(x<=mid) update(LS,l,mid,x,y,p); if(y>mid) update(RS,mid+1,r,x,y,p); pushup(rt,l,r); return; } int query(int rt,int l,int r,int LEN){ if(l==r)return l; //一个点,因为一直优先往左,所以这个点一定是最靠左的 int mid=l+r>>1; pushdown(rt,l,r); if(MaxiLen[LS]>=LEN) return query(LS,l,mid,LEN); if(last[LS]+pre[RS]>=LEN) return mid-last[LS]+1; //取得左儿子后继的开端 if(MaxiLen[RS]>=LEN) return query(RS,mid+1,r,LEN); return 0; //if(MaxiLen[rt]<LEN)return 0; } int main(){ scanf("%d%d",&n,&m); build(1,1,n); int ty,x,L; while(m--){ scanf("%d",&ty); if(ty==1){ scanf("%d",&x); L=query(1,1,n,x); printf("%d\n",L); if(L)update(1,1,n,L,L+x-1,1); }else { scanf("%d%d",&L,&x); update(1,1,n,L,L+x-1,0); } } return 0; }
颜色是不是很好看qwq
End