洛谷 P2894 [USACO08FEB]酒店Hotel(线段树区间合并)

线段树维护区间内容的一道经典题,值得一做,废话少说,开始讲解吧。

1.线段树维护内容
线段树维护一个最长区间。
线段树节点维护4个东西:
1.必须从左节点延伸的最长长度(lm)
2.必须从右节点延伸的最长长度(rm)
3.这一段的最长未占用长度(mm)
5.一个lazy标记,用于线段树的yy (laz = 0/1/2 <不需下放/需下放住房/需下放退房>)


2.区间内容的合并
对于左右儿子的合并(fa = lc+rc)
1.如果lc左侧全是空房,那么fa.lm为lc.sum+rc.lm ;
2.如果不是全为空房,那么fa.lm = lc.lm ;
3.对于fa.rm,右侧最大延展,类似于左侧延展。
4.对于区间最大延伸,fa.mm = max( max(fa.lm,fa.rm) , (lc.rm+rc.lm) , max(lc.mm,rc.mm)) ;


3.信息修改的处理
对于修改(住房及退房)<[target left , target right]>

一.住房:
1.完全包含状况(tl<=l && r<=tr),区间的lm、rm、mm、sum全部变为0 ; laz = 1;
2.不完全包含情况递归处理左右子树

二.退房:
1.完全包含状况(tl<=l && r<=tr),区间的lm、rm、mm、sum全部变为(l-r+1) ; laz = 2;
2.不完全包含情况递归处理左右子树

三.pushdown_lazy的操作
1.在处理节点之前进行。
2.将fa.laz恢复为0。
3.对于laz == 1\2 的情况,同住房\退房的方法处理


4.查询答案的方法

对于询问、查询<[target left , target right], target length == K >
1.查询左儿子lc.mm是否大于K
-大于等于K:递归左儿子查找位置,返回。
-小于K,进入下一步
2.判断lc.rm + rc.lm 是否大于K
-大于等于K:直接返回(midpos - lc.rm + 1)的左端点位置
-小于K,进入下一步
3.判断右儿子rc.mm是否大于K
-大于等于L:递归右儿子查找位置,返回。
-小于K:直接返回,输出无解(0)


5.具体实现代码:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#define lc ro<<1
#define rc ro<<1|1
#define ll long long
#define maxn 50010
using namespace std;

inline int gi()
{
  int date = 0,m = 1; char ch = 0;
  while(ch!='-'&&(ch<'0'||ch>'9'))ch = getchar();
  if(ch=='-'){m = -1; ch = getchar();}
  while(ch>='0' && ch<='9')
    {
      date = date*10+ch-'0';
      ch = getchar();
    }return date*m;
}
inline void write(int ac)
{
  if(ac>9)write(ac/10);
  putchar(ac%10+'0');
}

struct segment_tree{int lm,rm,mm,laz;};
segment_tree t[maxn<<3+5];
int flag,pos,L,R,len,order;

void merge_item(int ro,int l,int r)
{
  int mid = (l+r)>>1;
  if(t[lc].mm == (mid-l+1))t[ro].lm = t[lc].mm+t[rc].lm;
  else t[ro].lm = t[lc].lm;
  if(t[rc].mm == (r-mid))t[ro].rm = t[rc].mm+t[lc].rm;
  else t[ro].rm = t[rc].rm;
  int mlc = max( t[lc].mm , t[rc].mm );
  mlc = max( mlc , max( t[ro].lm , t[ro].rm ) );
  t[ro].mm = max( mlc , t[lc].rm + t[rc].lm);
}

void pushdown(int ro,int l,int r)
{
  if(t[ro].laz == 1)
    {
      t[lc].lm = t[rc].lm = t[lc].rm = t[rc].rm = 0;
      t[lc].mm = t[rc].mm = 0;
      t[lc].laz = t[rc].laz = 1;
    }
  else if(t[ro].laz == 2)
    {
      int mid = (l+r)>>1;
      t[lc].lm = t[lc].rm = t[lc].mm = mid-l+1;
      t[rc].lm = t[rc].rm = t[rc].mm = r-mid;
      t[lc].laz = t[rc].laz = 2;
    }
  t[ro].laz = 0; return;
}

void build(int ro,int l,int r)
{
  if(l == r){
    t[ro].lm = t[ro].rm = t[ro].mm = 1;
    t[ro].laz = 0; return;
  }
  int mid = (l+r)>>1;
  build(lc,l,mid);  build(rc,mid+1,r);
  merge_item(ro,l,r);  return;
}

void query(int ro,int l,int r,int tlen)
{
  if(t[ro].laz)pushdown(ro,l,r);
  if(l == r){flag = 1; pos = l; return;}
  int mid = (l+r)>>1;
  if(t[lc].mm >= tlen)
    query(lc,l,mid,tlen);
  else if(t[lc].rm + t[rc].lm >= tlen && !flag)
    {flag = 1; pos = mid - t[lc].rm + 1; return;}
  else if(t[rc].mm>=tlen && !flag)
    query(rc,mid+1,r,tlen);
  merge_item(ro,l,r);
}

void update(int ro,int l,int r,int tl,int tr,int ord)
{
  if(t[ro].laz)pushdown(ro,l,r);
  if(tl>r || tr<l)return;
  if(tl<=l && r<=tr)
    {
      if(ord == 1){
    t[ro].lm = t[ro].rm = t[ro].mm = 0;
    t[ro].laz = 1; return;
      }
      else if(ord == 2){
        t[ro].lm = t[ro].rm = t[ro].mm = r-l+1;
    t[ro].laz = 2; return;
      }
    }
  int mid = (l+r)>>1;
  update(lc,l,mid,tl,tr,ord);
  update(rc,mid+1,r,tl,tr,ord);
  merge_item(ro,l,r); return;
}
/*
void recheck(int ro,int l,int r)
{
  if(t[ro].laz)pushdown(ro,l,r);
  if(l == r){write(t[ro].mm);putchar(' ');return;}
  int mid = (l+r)>>1;
  recheck(lc,l,mid);
  recheck(rc,mid+1,r);
  return;
}
*/
int main()
{
  freopen("Hotel.in","r",stdin);
  int n = gi(),m = gi();
  build(1,1,n);
  while(m -- )
    {
      order = gi();
      if(order == 1)
    {
      flag = 0; pos = 0;
      len = gi();
      query(1,1,n,len);
      write(pos); putchar('\n');
          //recheck(1,1,n); cout<<endl;   //检查函数
      if(pos)update(1,1,n,pos,pos+len-1,1);
      //recheck(1,1,n); cout<<endl;
    }
      else if(order == 2)
      {
    L = gi(); R = L + gi() - 1;
    update(1,1,n,L,R,2);
      }
    }return 0;
}

Hahahahaha!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值