[POJ3667]Hotel(线段树)

    xiaoxiao2021-09-17  77

    === ===

    这里放传送门

    === ===

    题解

    这道题的意思是动态维护连续为0的个数然后返回最靠左的长度大于给定的x的一段连续的0区间。记得当时ATP它们老师考这个题的时候ATP一时脑抽差点写出平衡树来23333实际上这是一个线段树的经典问题啦。

    只要在线段树的节点里面维护三个量——最靠左的连续区间,最靠右的连续区间和总的连续区间就可以了。反正就是在update的时候麻烦的讨论一下判断一下左右子树的继承关系。。在查找的时候,find过程返回找到的区间的左端点。仍然先判断左子树的tot是否大于x,是的话就进入左子树查找,否则判断这个区间是否跨了两个半区间,也就是左子树的right加上右子树的left;再否则就进入右子树查询。

    代码

    #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,ans,L,R; struct segtree{ int tot,lt,rt,dlt; segtree(){tot=lt=rt=0;dlt=-1;} }t[2000010]; void update(int i,int l,int r){ int mid=(l+r)>>1; t[i].lt=t[i<<1].lt; t[i].rt=t[(i<<1)+1].rt; if (t[i<<1].lt==mid-l+1) t[i].lt+=t[(i<<1)+1].lt;//注意判断是否扩展 if (t[(i<<1)+1].rt==r-mid) t[i].rt+=t[i<<1].rt; t[i].tot=max(max(t[i<<1].tot,t[(i<<1)+1].tot),t[i<<1].rt+t[(i<<1)+1].lt); } void pushdown(int i,int l,int r){ if (t[i].dlt!=-1){ int mid=(l+r)>>1,lc=i<<1,rc=(i<<1)+1; t[lc].dlt=t[i].dlt; t[lc].tot=t[lc].lt=t[lc].rt=t[i].dlt*(mid-l+1); t[rc].dlt=t[i].dlt; t[rc].tot=t[rc].lt=t[rc].rt=t[i].dlt*(r-mid); t[i].dlt=-1; } } void build(int i,int l,int r){ if (l==r){ t[i].tot=t[i].lt=t[i].rt=1; t[i].dlt=-1; return; } int mid=(l+r)>>1; build(i<<1,l,mid); build((i<<1)+1,mid+1,r); update(i,l,r); } void change(int i,int l,int r,int left,int right,int v){ if ((left<=l)&&(right>=r)){ t[i].tot=t[i].lt=t[i].rt=v*(r-l+1); t[i].dlt=v; return; } int mid=(l+r)>>1; pushdown(i,l,r); if (left<=mid) change(i<<1,l,mid,left,right,v); if (right>mid) change((i<<1)+1,mid+1,r,left,right,v); update(i,l,r); } int find(int i,int l,int r,int x){ if (l==r) return l; int mid=(l+r)>>1; pushdown(i,l,r); if (t[i<<1].tot>=x) return find(i<<1,l,mid,x); else if (t[i<<1].rt+t[(i<<1)+1].lt>=x) return mid-t[i<<1].rt+1; else return find((i<<1)+1,mid+1,r,x); } int main() { scanf("%d%d",&n,&m); build(1,1,n); for (int i=1;i<=m;i++){ int o; scanf("%d",&o); if (o==1){ int x; scanf("%d",&x); if (t[1].tot<x) printf("0\n"); else{ L=find(1,1,n,x); printf("%d\n",L); R=L+x-1; change(1,1,n,L,R,0); } }else{ int x,y; scanf("%d%d",&x,&y); change(1,1,n,x,x+y-1,1); } } return 0; }

    偏偏在最后出现的补充说明

    实际上话说回来一开始写的时候还差点把find过程里面再套一个二分写成愚蠢的 O(nlog2n) ,不过线段树的过程本身就相当于二分了啊好不好所以有些时候二分是可以借助线段树来完成的!

    转载请注明原文地址: https://ju.6miu.com/read-677661.html

    最新回复(0)