PKU 3667

Hotel

题型:线段树(设计并维护复杂域)

描述:旅店登记,1.找一段最靠前的连续w个空房间;2.退订[x,x-d+1]段的房间。

思路:

1. 域的设计

struct Seg {

    int l, r;

    int lx, rx, mx;

    char cv;

};

lx :左端连续空房间数

rx: 右端连续空房间数

mx: 连续最大空房间数

cv : = 0,全空, = 1 全满, = -1 非空非满。

2. 域的维护

开房:先用find(w, 1); 找到位置 rom, 然后用 mody(rom, rom+w-1, 0, 1); 进行插入操作

退房:进行删除操作mody(x, x-d+1, 1, 1)

因为此题区间的修改(插入和删除)很相似,所以用一个函数实现,0表示插入,1表示删除。

对cv域的维护包括   1. t[k].l == l && t[k].r == r 。 2. 根据父节点的cv值对子节点的修改(关键),find()和mody()中都需要有。代码:

 

 
  
  if (t[k].cv >= 0 ) {
t[k0].cv
= t[k0 + 1 ].cv = t[k].cv;
if (t[k].cv) p1 = p2 = 0 ;
else { p1 = t[k0].r - t[k0].l + 1 , p2 = t[k0 + 1 ].r - t[k0 + 1 ].l + 1 ; }
t[k0].mx
= t[k0].lx = t[k0].rx = p1;
t[k0
+ 1 ].mx = t[k0 + 1 ].lx = t[k0 + 1 ].rx = p2;
t[k].cv
= - 1 ;
}

 

 

 

lx, rx, mx需要在回溯的过程维护:mx 为 两个子节点的mx和交叉区间和的最大值,

父节点的lx = 左子节点的lx + (右子节点的lx);

父节点的rx = 右子节点的rx + (左子节点的rx);

当左右子节点的lx,rx为整个区间长时,()需加上。

 

 
  
t[k].mx = MAX(t[k0].mx, MAX(t[k0 + 1 ].mx, t[k0].rx + t[k0 + 1 ].lx));
t[k].lx
= t[k0].lx;
t[k].rx
= t[k0 + 1 ].rx;
if (t[k0].lx == t[k0].r - t[k0].l + 1 ) t[k].lx += t[k0 + 1 ].lx;
if (t[k0 + 1 ].rx == t[k0 + 1 ].r - t[k0 + 1 ].l + 1 ) t[k].rx += t[k0].rx;

 

 

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
// 3236K 610MS
#include < stdio.h >
#include
< string .h >
#define MIN(x,y) ((x)<(y)?(x):(y))
#define MAX(x,y) ((x)>(y)?(x):(y))
#define NL 65536

struct Seg {
int l, r;
int lx, rx, mx; // 左端连续空房数,右端连续空房数,最大连续空房数
char cv; // 1 满 0 空 -1 非空非满
}t[NL * 2 ];
int rom;

void build( int l, int r, int k)
{
t[k].l
= l;
t[k].r
= r;
t[k].lx
= t[k].rx = t[k].mx = r - l + 1 ;
t[k].cv
= 0 ;

int md = (l + r) >> 1 , k0 = k << 1 ;
if (l < r) {
build(l, md, k0);
build(md
+ 1 , r, k0 + 1 );
}
}

// flg == 0 插入,flg == 1 删除
void mody( int l, int r, bool flg, int k)
{
if (t[k].l == l && t[k].r == r) {
if (flg) {
t[k].lx
= t[k].rx = t[k].mx = t[k].r - t[k].l + 1 ;
}
else {
t[k].lx
= t[k].rx = t[k].mx = 0 ;
}
t[k].cv
= flg ^ 1 ;
return ;
}

int md = (t[k].l + t[k].r) >> 1 , k0 = k << 1 , p1, p2;
if (t[k].cv >= 0 ) {
t[k0].cv
= t[k0 + 1 ].cv = t[k].cv;
if (t[k].cv) p1 = p2 = 0 ;
else { p1 = t[k0].r - t[k0].l + 1 , p2 = t[k0 + 1 ].r - t[k0 + 1 ].l + 1 ; }
t[k0].mx
= t[k0].lx = t[k0].rx = p1;
t[k0
+ 1 ].mx = t[k0 + 1 ].lx = t[k0 + 1 ].rx = p2;
t[k].cv
= - 1 ;
}
if (r <= md)
mody(l, r, flg, k0);
else if (l > md)
mody(l, r, flg, k0
+ 1 );
else {
mody(l, md, flg, k0);
mody(md
+ 1 , r, flg, k0 + 1 );
}
t[k].mx
= MAX(t[k0].mx, MAX(t[k0 + 1 ].mx, t[k0].rx + t[k0 + 1 ].lx));
t[k].lx
= t[k0].lx;
t[k].rx
= t[k0 + 1 ].rx;
if (t[k0].lx == t[k0].r - t[k0].l + 1 ) t[k].lx += t[k0 + 1 ].lx;
if (t[k0 + 1 ].rx == t[k0 + 1 ].r - t[k0 + 1 ].l + 1 ) t[k].rx += t[k0].rx;
}

void find( int w, int k)
{
if (t[k].l == t[k].r) {
rom
= t[k].l;
return ;
}
int md = (t[k].l + t[k].r) >> 1 , k0 = k << 1 , p1, p2;
if (t[k].cv >= 0 ) {
t[k0].cv
= t[k0 + 1 ].cv = t[k].cv;
if (t[k].cv) p1 = p2 = 0 ;
else { p1 = t[k0].r - t[k0].l + 1 , p2 = t[k0 + 1 ].r - t[k0 + 1 ].l + 1 ; }
t[k0].mx
= t[k0].lx = t[k0].rx = p1;
t[k0
+ 1 ].mx = t[k0 + 1 ].lx = t[k0 + 1 ].rx = p2;
t[k].cv
= - 1 ;
}
if (t[k0].mx >= w)
find(w, k0);
else if (t[k0].rx + t[k0 + 1 ].lx >= w)
rom
= t[k0].r - t[k0].rx + 1 ;
else if (t[k0 + 1 ].mx >= w)
find(w, k0
+ 1 );
}

int main()
{
int n, m;
int c, x, d;
// freopen("hotel2.in", "r", stdin);
while (scanf( " %d%d " , & n, & m) != EOF) {
build(
1 , n, 1 );
while (m -- ) {
scanf(
" %d " , & c);
if (c == 1 ) {
scanf(
" %d " , & d);
rom
= 0 ;
find(d,
1 );
printf(
" %d\n " , rom);
if (rom) {
mody(rom, rom
+ d - 1 , 0 , 1 );
}
}
else {
scanf(
" %d%d " , & x, & d);
mody(x, x
+ d - 1 , 1 , 1 );
}
}
}
return 0 ;
}

 

 

一组数据

思路参考:北邮人

代码参考:傻崽NOCOW

转载于:https://www.cnblogs.com/superbin/archive/2010/07/18/1780194.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值