(本人本题完成于2016-7-22)
题目大意:有一个旅馆有N个房间,标号为1,2,...,n,刚开始它们都是空着的。有M个操作,分为两种:1.找到标号最小的连续的D个房间并入住,并输出这些房间中标号最小的房间的标号,如果找不到这样的连续房间则输出0。2.清空从X号房间开始的D个房间。
做法:建一棵线段树,除区间左右端点外维护:sum:该区间内的最大连续空区间长度。lsum:该区间的最左边的连续空区间长度。rsum:该区间的最右边的连续空区间长度。p:标记该区间是否全空(p=1)或全满(p=0),p=-1时表示没有标记。
以下是本人代码:
#include <cstdio> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; int n,m,o,a,x; struct node { int l,r; int lsum,rsum,sum,p; }seg[200010]; void buildtree(int no,int l,int r) //建立线段树 { int mid=(l+r)/2; seg[no].l=l;seg[no].r=r; seg[no].sum=seg[no].lsum=seg[no].rsum=r-l+1; seg[no].p=-1; if (l!=r) { buildtree(2*no,l,mid); buildtree(2*no+1,mid+1,r); } } void pushdown(int no) //将节点no的标记下放 { if (seg[no].p!=-1) { seg[2*no].p=seg[2*no+1].p=seg[no].p; if (!seg[2*no].p) { seg[2*no].lsum=seg[2*no].rsum=seg[2*no].sum=0; seg[2*no+1].lsum=seg[2*no+1].rsum=seg[2*no+1].sum=0; } else { seg[2*no].lsum=seg[2*no].rsum=seg[2*no].sum=seg[2*no].r-seg[2*no].l+1; seg[2*no+1].lsum=seg[2*no+1].rsum=seg[2*no+1].sum=seg[2*no+1].r-seg[2*no+1].l+1; } seg[no].p=-1; } } void pushup(int no) //用更新后的no的儿子的数值来更新节点no { seg[no].lsum=seg[2*no].lsum; seg[no].rsum=seg[2*no+1].rsum; if (seg[2*no].lsum==seg[2*no].r-seg[2*no].l+1) seg[no].lsum+=seg[2*no+1].lsum; if (seg[2*no+1].rsum==seg[2*no+1].r-seg[2*no+1].l+1) seg[no].rsum+=seg[2*no].rsum; seg[no].sum=max(max(seg[2*no].sum,seg[2*no+1].sum),seg[2*no].rsum+seg[2*no+1].lsum); } int query(int no,int a) //查询并返回标号最小的长度为a的连续空区间的左端点标号 { int mid=(seg[no].l+seg[no].r)/2; if (seg[no].l==seg[no].r) return seg[no].l; pushdown(no); if (seg[2*no].sum>=a) return query(2*no,a); if (seg[2*no].rsum+seg[2*no+1].lsum>=a) return mid-seg[2*no].rsum+1; if (seg[2*no+1].sum>=a) return query(2*no+1,a); return -1; } void checkin(int no,int s,int t) //将区间(s,t)覆盖 { int mid=(seg[no].l+seg[no].r)/2; if (seg[no].l>=s&&seg[no].r<=t) { seg[no].lsum=seg[no].rsum=seg[no].sum=0; seg[no].p=0; return; } pushdown(no); if (s<=mid) checkin(2*no,s,t); if (t>mid) checkin(2*no+1,s,t); pushup(no); } void checkout(int no,int s,int t) //清空区间(s,t) { int mid=(seg[no].l+seg[no].r)/2; if (seg[no].l>=s&&seg[no].r<=t) { seg[no].lsum=seg[no].rsum=seg[no].sum=seg[no].r-seg[no].l+1; seg[no].p=1; return; } pushdown(no); if (s<=mid) checkout(2*no,s,t); if (t>mid) checkout(2*no+1,s,t); pushup(no); } int main() { scanf("%d %d",&n,&m); buildtree(1,1,n); for(int i=1;i<=m;i++) { scanf("%d",&o); if (o==1) { scanf("%d",&a); if (seg[1].sum<a) printf("0\n"); //如果没有那么长的连续空区间,输出0 else { int f=query(1,a); printf("%d\n",f); checkin(1,f,f+a-1); } } if (o==2) { scanf("%d %d",&a,&x); checkout(1,a,a+x-1); } } return 0; }