uestc oj 1227 Hotel

Hotel
http://www.acm.uestc.edu.cn/problem.php?pid=1227



  本题运用线段树加懒操作解决。属于查询区间,更新区间类。
  注意:本题的懒操作标记直接运用节点内的信息,并且在查询是不必关心懒操作,只是在更新时注意即可。后面解释
 
 1:定义结点内所存放的数据(用一个结构体),存放结点左端点left,结点右端点right,以及结点内的最大连续空房数量,自最左端left开始的最大空房数lemp,自最右端right开始的最大空房数remp和结点内的最大空房数nemp。
  
  2:首先建树,初始化结点信息,最初所有房间都是空的,所以nemp=lemp=remp=len;
           然后根据输入查询并更新树。(每次查询更新时都是自根节点自上往下递归更新的,并且按照自左往右以保证左起点最小)
  
  3:查询:在查询时根据查询长度从根节点1开始查询。
        首先判断根节点是否包括此大小的连续空房数,若不存在(tree[id].nemp<len)则退出返回0 存在则继续往下查询(自左往右)
        其后又一个判断,若节点的左边left等于右边right,则表明是叶子节点,而又满足了第一个判断(tree[id].nemp>=len所以可以得出结果就是此叶子节点的左边界(其实也是右边界,相等嘛,此时查询长度为1)。
        然自左往右查询,如果根节点的lemp>=len,则结果便是tree[id].left,(因为lemp自left开始),若不满足,则递归查询左子树(tree[id*2].nemp>=len),若仍不满足则查询左子树与右子树相邻的区间内是否有连续的空房数满足结果,若仍不能,则查询递归查询右子树。
  4:更新:更新的主要操作就是把满足更新区间的区间全部将nemp,lemp ,remp 置空或者置满。仍是从根节点开始的
       首先判断此节点区间范围是否在查询区间内,若在则将其区间置空或者置满(依照形参其实此时置空或者置满之后就return了,懒操作标记完成,用于后面更新)
      下面检查懒操作标记,若满足则继续更新其左右子树给其左右子树加懒操作标记。
      然后按照常规方法更新计科,判断左边还是右边或者是中间递归更新就可以了。
      递归后还有操作,其实后面的操作是保证能够向上更新的。而懒操作就是保证能够向下更新的。
      向上更新是需要注意的是如果此节点的lemp是自己左子树的区间长度的话还有加上右子树的lemp,同理如果remp是自己右子树的区间长度的话还要加上自己做子树的remp。


下面解释为什么在查询是不用关心懒操作,因为此题要求的是查询最大连续空房,父节点都不能够满足,自然不用关心子节点了。




时间复杂度:线段树的修改操作和查询操作都是logN,一共执行Q次修改和查询操作  故时间复杂度为O(QlogN)
空间复杂度:线段树用堆实现   故空间复杂度为O(3N) 有种说法是 3N属于正常,4N属于保守,2N属于拼人品

当然本题用了4N的



#include<cstdio>
#define INF 500005
#define L(x) (x<<1)
#define R(x) (x<<1|1)
#define MID(x,y)  ((x+y)>>1)
#define MAX(x,y)  (x>y?x:y)
#define LEN(x,y)  (y-x+1)
using namespace std;

struct node
{
    int left,right;
    int lemp,remp,nemp;
};
node tree[INF*4];
int n,m,a,b,c;



void init(int id,int val)
{
     int len = LEN(tree[id].left,tree[id].right);
     tree[id].nemp = len*val;
     tree[id].lemp = tree[id].nemp;
     tree[id].remp = tree[id].nemp;
}

void createTree(int left,int right,int id)
{
    tree[id].left = left;
    tree[id].right = right;
    init(id,1);
    if(left == right) return;
    int mid = MID(left,right);
    createTree(left,mid,L(id));
    createTree(mid+1,right,R(id));
}

int search(int len,int id)
{
    if(tree[id].nemp <len) return 0;
    if(tree[id].left == tree[id].right) return tree[id].left;
    if(tree[id].lemp>=len) return tree[id].left;
    if(tree[id*2].nemp>=len) return search(len,id*2);
    else if(tree[id*2].remp+tree[id*2+1].lemp>=len)
         return tree[id*2].right - tree[id*2].remp +1;
    else return search(len,id*2+1);
}

void update(int left,int right,int id,int val)
{
    if(tree[id].left>=left&&tree[id].right<=right)
    {
       init(id,val);
       return ;
    }

    int len = LEN(tree[id].left,tree[id].right);
    if(tree[id].nemp ==len||tree[id].nemp ==0)  //懒操作更新  向下
    {
        int v;
        if(tree[id].nemp==len) v = 1;
        else v =0;
        init(id*2,v);
        init(id*2+1,v);
    }
    int mid = MID(tree[id].left,tree[id].right);
    if(right<=mid) update(left,right,id*2,val);
    else if(left>mid) update(left,right,id*2+1,val);
    else
    {
        update(left,mid,id*2,val);
        update(mid+1,right,id*2+1,val);
    }
                                                              
                                                         //递归时向上更新
    int len1 =LEN(tree[L(id)].left,tree[L(id)].right);
    tree[id].lemp = tree[L(id)].lemp;
    if(tree[id].lemp == len1)
    tree[id].lemp +=tree[R(id)].lemp;


    int len2 = LEN(tree[R(id)].left,tree[R(id)].right);
    tree[id].remp = tree[R(id)].remp;
    if(tree[id].remp ==len2)
    tree[id].remp +=tree[L(id)].remp;

    tree[id].nemp = tree[R(id)].lemp + tree[L(id)].remp;
    tree[id].nemp = MAX(tree[id].nemp,tree[L(id)].nemp);
    tree[id].nemp = MAX(tree[id].nemp,tree[R(id)].nemp);
}

int main()
{
     //freopen("1.txt","r",stdin);
    while(scanf("%d%d",&n,&m)==2)
    {
        createTree(1,n,1);
        while(m--)
        {
            scanf("%d",&a);
            if(a==1)
            {
                scanf("%d",&b);
                int ans = search(b,1);
                printf("%d\n",ans);
                if(ans)
                update(ans,ans+b-1,1,0);
            }
            else
            {
                scanf("%d%d",&b,&c);
                update(b,b+c-1,1,1);
            }

        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值