• pku 3667 Hotel


    pku 3667 Hotel

    题意:

    去掉题目背景,就是给你一个[1,n]的区间,初始时,整个区间为空,在区间进行俩个操作:

    1) 输入 1 D :在区间上从左到右找出第一个连续的长度为D 的空间,并将该区间填满,输出区间的端点,若不存在,输出0

    2) 输入 2 a b: 将区间[a,a+b-1] 填满

    分析:很明显是用线段树进行维护,关键是每一个节点需要保存该区间的些什么值?

    这里我们可以想到,因为要找出的是一个连续的空区间的左端点,这个区间可能分布在左儿子上,右儿子上,或者可能在俩边都有,这样给查询带来了极大的不方便。不过,我们可以充分利用线段树的结构特点,通过保存与区间左右端点有关的信息,就可以遍历整个区间的信息。

    我们给每一个节点添加四个域,一个保存该区间是否整个被填满,一个域保存该区间存在的最大的连续的区间长度,一个域保存该区间以左端点开始的空区间的长度,一个域保存该区间以右端点结束的空区间的长度;

    这样,我们通过维护线段树每一个域的值,就可以得到整个区间内每一个连续的空区间的长度;

    维护过程头脑要异常的清醒啊!!!!

    #include<iostream>
    #include<algorithm>
    #define MAXN 50005
    using namespace std;
    struct node
    {
    	int ls,rs,ms,c;
    }p[MAXN<<2];
    void PushDown(int k,int m)
    {
    	int kl=k<<1,kr=kl+1;
    	if(p[k].c!=-1)
    	{
    		p[kl].c=p[kr].c=p[k].c;
    		p[kl].ls=p[kl].rs=p[kl].ms= p[k].c? 0: m- (m>>1);
    		p[kr].ls=p[kr].rs=p[kr].ms= p[k].c? 0: (m>>1);
    		p[k].c=-1;
    	}
    }
    void PushUp(int k,int m)//除了查询之外,难点就在于这一步维护了,将子区间的信息传递给回来
    {
    	int kl=k<<1,kr=kl+1;
    	p[k].ls=p[kl].ls;
    	p[k].rs=p[kr].rs;
    	if(p[kl].ls== m-(m>>1))//若左儿子整个为空区间,则可以将右儿子的左连续空间长度加到整个空间的左连续空间长度
    		p[k].ls+=p[kr].ls;
    	if(p[kr].rs== m>>1)
    		p[k].rs+=p[kl].rs;
    	p[k].ms=max(p[kl].rs+p[kr].ls,max(p[kl].ms,p[kr].ms));//整个区间存在的最长的空区间长度等于 左连续区间,右连续区间,或中间存在的最大长度的空区间中的最大值
    }
    void build(int k,int s,int t)//建树
    {
    	p[k].ls=p[k].rs=p[k].ms=t-s+1;
    	p[k].c=-1;
    	if(s==t) return ;
    	int kl=k<<1,kr=kl+1,mid=(s+t)>>1;
    	build(kl,s,mid);
    	build(kr,mid+1,t);
    }
    void update(int k,int c,int s,int t,int l,int r)//根据c的值,将区间[l,r]填满或清空
    {
    	if(l<=s && t<=r)
    	{
    		p[k].c=c;
    		p[k].ms=p[k].ls=p[k].rs=c? 0: t-s+1;
    		return ;
    	}
    	int kl=k<<1,kr=kl+1,mid=(s+t)>>1;
    	PushDown(k,t-s+1);
    	if(l<=mid) update(kl,c,s,mid,l,r);
    	if(r>mid) update(kr,c,mid+1,t,l,r);
    	PushUp(k,t-s+1);
    }
    int query(int k,int w,int s,int t)
    {
    	if(s==t) return s;
    	PushDown(k,t-s+1);
    	int kl=k<<1,kr=kl+1,mid=(s+t)>>1;
    	if(p[kl].ms>=w) //左区间本身存在着长度大于w的空区间
    		return query(kl,w,s,mid);
    	else if(p[kl].rs+p[kr].ls>=w) //保存左右端点空区间长度的目的就在于此了,p[kl].rs+p[kr].ls 是区间内与中点有关的连续的空区间的长度
    		return mid-p[kl].rs+1;
    	return query(kr,w,mid+1,t);
    }
    int main()
    {
    	int n,m;
    	int a,b,c;
    	scanf("%d %d",&n,&m);
    	build(1,1,n);
    	while(m--)
    	{
    		scanf("%d",&c);
    		if(c==1)
    		{
    			scanf("%d",&a);
    			if(p[1].ms<a)//整个区间内的最大长度的空区间小于a
    				puts("0");
    			else 
    			{
    				b=query(1,a,1,n);
    				printf("%d\n",b);
    				update(1,1,1,n,b,b+a-1);
    			}
    		}
    		else 
    		{
    			scanf("%d %d",&a,&b);
    			update(1,0,1,n,a,a+b-1);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    [Oracle工程师手记]如何收集 RMAN 工作时的 10046 trace
    [Linux]Linux环境,如何查找一个目录下所有包含特定字符串的文件
    [Oracle工程师手记]如何找到 RMAN 的所有 session
    PowerShell提交HTTP Request Post请求
    PowerShell计算字符串MD5 Hash值
    《异类》笔记
    《哈弗商学院 判断与决策心理学课》笔记
    《影响力》笔记
    《金字塔原理》笔记
    三月十九日,偶感
  • 原文地址:https://www.cnblogs.com/nanke/p/2185840.html
Copyright © 2020-2023  润新知