POJ 3667 线段树区间合并裸题
题意:给一个n和m,表示n个房间,m次操作,操作类型有2种,一种把求连续未租出的房间数有d个的最小的最左边的房间号,另一个操作时把从x到x+d-1的房间号收回。
建立线段树,值为1表示未租出,0为租出,线段树实现区间合并,必须记录区间左右端点的状态,这样才能实现区间合并,用lsum记录以左端点开始的最长连续可租房间数,
rsum记录右端点开始的最长连续可组房间数,msum记录该区间的最长连续可租房间数,那么,区间合并时,根节点的更新 msum = max(rsum[rt<<1]+lsum[rt<<1|1],max(msum[rt<<1],msum[rt<<1|1]));
另外还需lazy节点标记区间更新
#include <iostream> #include <cstdio> #include <cstring> #define N 50005 using namespace std; int lsum[N<<2],rsum[N<<2],msum[N<<2],lazy[N<<2]; void pushup(int l,int r,int rt) { int mid=(l+r)>>1; lsum[rt]=lsum[rt<<1]; if(lsum[rt]==mid-l+1)lsum[rt]+=lsum[rt<<1|1]; rsum[rt]=rsum[rt<<1|1]; if(rsum[rt]==r-mid)rsum[rt]+=rsum[rt<<1]; msum[rt]=max(rsum[rt<<1]+lsum[rt<<1|1],max(msum[rt<<1],msum[rt<<1|1])); } void pushdown(int l,int r,int rt) { if(lazy[rt]!=-1) { int mid=(l+r)>>1; lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt]; msum[rt<<1]=lsum[rt<<1]=rsum[rt<<1]=lazy[rt]?mid-l+1:0;// lazy为1表示全部转为1,为0表示全部转为0 msum[rt<<1|1]=lsum[rt<<1|1]=rsum[rt<<1|1]=lazy[rt]?r-mid:0; lazy[rt]=-1; } } void build(int l,int r,int rt) { lazy[rt]=-1; lsum[rt]=rsum[rt]=msum[rt]=r-l+1; if(l==r)return; int mid=(l+r)>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); } void update(int L,int R,int k,int l,int r,int rt) { if(L<=l&&r<=R) { lazy[rt]=k; msum[rt]=lsum[rt]=rsum[rt]=k?r-l+1:0; return; } pushdown(l,r,rt); int mid=(l+r)>>1; if(L<=mid)update(L,R,k,l,mid,rt<<1); if(R>mid)update(L,R,k,mid+1,r,rt<<1|1); pushup(l,r,rt); } int query(int len,int l,int r,int rt) { if(l==r)return l; pushdown(l,r,rt); int mid=(l+r)>>1; if(msum[rt<<1]>=len)return query(len,l,mid,rt<<1); else if(rsum[rt<<1]+lsum[rt<<1|1]>=len)return mid-rsum[rt<<1]+1; else return query(len,mid+1,r,rt<<1|1); } int main() { int n,m; scanf("%d%d",&n,&m); build(1,n,1); while (m--) { int k,x,d; scanf("%d",&k); if(k==1) { scanf("%d",&d); if(d>msum[1])printf("0\n"); else { int l=query(d,1,n,1); printf("%d\n",l); update(l,l+d-1,0,1,n,1); } } else { scanf("%d%d",&x,&d); update(x,x+d-1,1,1,n,1); } } return 0; }