链接:点击打开链接
题意:有N个连续的空房间,分别编号1~N,M条指令,指令分为两种,分别为1 D和2 X D,第一种指令输出是否有D个连续的空房间,如果有则输出最靠左的第一个房间编号,否则则输出0,第二种则将从X开始D个房间清空
代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> using namespace std; const int SIZE=50005; int sum[SIZE<<2],lsum[SIZE<<2],rsum[SIZE<<2]; //sum表示当前节点所代表的区间最长连续空位 int add[SIZE<<2]; //lsum表示当前节点所代表的区间从左端起的最长连续空位 void pushdown(int rt,int m){ //rsum表示当前节点所代表的区间从右端起的最长连续空位 if(add[rt]!=-1){ add[rt<<1]=add[rt<<1|1]=add[rt]; sum[rt<<1]=lsum[rt<<1]=rsum[rt<<1]=add[rt]?0:(m-(m>>1)); sum[rt<<1|1]=lsum[rt<<1|1]=rsum[rt<<1|1]=add[rt]?0:(m>>1); add[rt]=-1; } } //懒惰标记跟区间更新几乎是一样的 void pushup(int rt,int m){ lsum[rt]=lsum[rt<<1]; rsum[rt]=rsum[rt<<1|1]; if(lsum[rt]==(m-(m>>1))) lsum[rt]+=lsum[rt<<1|1]; if(rsum[rt]==(m>>1)) rsum[rt]+=rsum[rt<<1]; sum[rt]=max(lsum[rt<<1|1]+rsum[rt<<1],max(sum[rt<<1],sum[rt<<1|1])); } //根据lsum和rsum判断sum void build(int l,int r,int rt){ int m; sum[rt]=lsum[rt]=rsum[rt]=r-l+1; add[rt]=-1; if(l==r) return; m=(l+r)>>1; build(l,m,rt<<1); build(m+1,r,rt<<1|1); } void update(int L,int R,int c,int l,int r,int rt){ int m; if(L<=l&&r<=R){ sum[rt]=lsum[rt]=rsum[rt]=c?0:r-l+1; add[rt]=c; return; } pushdown(rt,r-l+1); m=(l+r)>>1; if(L<=m) update(L,R,c,l,m,rt<<1); if(R>m) update(L,R,c,m+1,r,rt<<1|1); pushup(rt,r-l+1); } //建树和更新和成段更新也几乎是一样的 int query(int w,int l,int r,int rt){ int m; if(l==r) return l; pushdown(rt,r-l+1); m=(r+l)>>1; if(sum[rt<<1]>=w) //因为题目要求尽可能靠左,因此先查询左区间 return query(w,l,m,rt<<1); else if(lsum[rt<<1|1]+rsum[rt<<1]>=w) //然后左右区间相连 return m-rsum[rt<<1]+1; return query(w,m+1,r,rt<<1|1); //最后查询右区间 } int main(){ int n,m,i,j,a,b,c,tmp; while(scanf("%d%d",&n,&m)!=EOF){ build(1,n,1); while(m--){ scanf("%d",&c); if(c==1){ scanf("%d",&a); if(sum[1]<a){ puts("0"); continue; } //没有时输出0 tmp=query(a,1,n,1); printf("%d\n",tmp); update(tmp,tmp+a-1,1,1,n,1); } else{ scanf("%d%d",&a,&b); update(a,a+b-1,0,1,n,1); } } } return 0; }