题型:线段树(设计并维护复杂域)
描述:旅店登记,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;
// 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 ;
}
思路参考:北邮人