线段树区间合并例题 Hotel G 关于pushup操作的理解

[USACO08FEB] Hotel G - 洛谷

题意

这是一个旅馆,开始时[1,n]这个区间上的房间都没有住人,你需要完成两个操作

1:找到连续的x间空房,并且住满人,如果找不到输出0

2:把[x,x+y-1]区间房间清空

分析

首先我会给出线段树维护的内容 然后会讲解他们是什么意思

#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N = 50007;
struct Node {
	int li, ri, ansl, ansr, ans, len, lazy;
}t[N << 2];

1.这是一个最经典的一个区间合并的例题,让我们来思考连续的x间空房应该怎么去维护

如图 这是答案可能来源的三个区间

这也是线段树区间合并的灵魂 ansl ans ansr 维护三个ans,当然可能ansl等于0 ans==ansl 这些情况均不影响结果

ansl表示:必须包含左端点的连续区间为0的长度

ansr表示:必须包含右端点的连续区间为0的长度

ans表示:这一段区间连续为0的最大长度

len表示:这一段区间的长度

那么 我们首先需要解决pushup操作

如果已知了孩子的ansl,ansr,ans,该怎么去上传给父节点?

父节点的ans

t[p].ans = max(t[lc].ans, t[rc].ans)

但是,还有一种情况:

如下图 t[p].ans可能来源于t[lc].ansr+t[rc].ansl

但是 这种合并有一个条件!也是很多解题没有提到的

因为ans代表的是连续为0的区间长度,所以再合并时,你必须首先保证我画箭头的两个点都是0,表示空房

然而这是很多题解并没有注意到的,这才是区间合并,中间不可能白白地给你合并,你必须满足中间合并的一个条件才能合并

这也就解释了线段树维护的li,ri表示什么意思

li:表示区间左端点是不是空房 

ri:表示区间右端点是不是空房 

当且仅当左儿子ri==右儿子li==0 区间才能合并 父节点ans才能取两边加起来的答案

if (t[lc].ri == t[rc].li && t[lc].ri == 0) {
	t[p].ans = max(t[lc].ansr + t[rc].ansl, max(t[lc].ans, t[rc].ans));
}
else {
	t[p].ans = max(t[lc].ans, t[rc].ans);
}

(但是这道题也不用去维护li,ri 因为如果li==1或者ri==1 你对应lans或者rans==0 不会对结果造成影响 但是我强烈建议你去维护他 这样你可以再以后的题目中能灵敏的知道中间如果要取到 必须满足什么条件)

父节点的ansl和ansr:

如果t[lc].ansl==t[lc].len 那么t[p].ansl可能还会有t[rc].ansr 前提是这段区间能够合并 如上

if (t[lc].ansl == t[lc].len && t[lc].ri == t[rc].li && t[lc].ri == 0) {
	t[p].ansl = t[lc].ansl + t[rc].ansl;
}
else {
	t[p].ansl = t[lc].ansl;
}
if (t[rc].ansr == t[rc].len && t[lc].ri == t[rc].li && t[lc].ri == 0) {
	t[p].ansr = t[rc].ansr + t[lc].ansr;
}
else {
	t[p].ansr = t[rc].ansr;
}

然后pushup别忘了维护li和ri

	t[p].li = t[lc].li; t[p].ri = t[rc].ri;

完整代码

#include<iostream>
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N = 50007;
//li:区间左端点是否空房
//ri:区间右端点是否空房
//ansl:必须包含左端点的连续为0的长度
//ansr:必须包含右端点的连续为0的长度
//len:区间长度
//lazy:懒标记
struct Node {
	int li, ri, ansl, ansr, ans, len, lazy;
}t[N << 2];
void pushup(int p, int l, int r) {
	int mid = l + r >> 1;
	if (t[lc].ri == t[rc].li && t[lc].ri == 0) {
		t[p].ans = max(t[lc].ansr + t[rc].ansl, max(t[lc].ans, t[rc].ans));
	}
	else {
		t[p].ans = max(t[lc].ans, t[rc].ans);
	}
	t[p].li = t[lc].li; t[p].ri = t[rc].ri;

	if (t[lc].ansl == t[lc].len && t[lc].ri == t[rc].li && t[lc].ri == 0) {
		t[p].ansl = t[lc].ansl + t[rc].ansl;
	}
	else {
		t[p].ansl = t[lc].ansl;
	}
	if (t[rc].ansr == t[rc].len && t[lc].ri == t[rc].li && t[lc].ri == 0) {
		t[p].ansr = t[rc].ansr + t[lc].ansr;
	}
	else {
		t[p].ansr = t[rc].ansr;
	}
}
void pushdown(int p, int l, int r) {
	int mid = l + r >> 1;
	if (t[p].lazy == 0) {//退房
		t[lc].ansl = t[lc].ansr = t[lc].ans = mid - l + 1;
		t[rc].ansl = t[rc].ansr = t[rc].ans = r - mid;
		t[lc].lazy = t[rc].lazy = t[p].lazy;
		t[lc].li = t[lc].ri = t[rc].li = t[rc].ri = 0;
		t[p].lazy = -1;
	}
	else if (t[p].lazy == 1) {//住房
		t[lc].ansl = t[lc].ansr = t[lc].ans = 0;
		t[rc].ansl = t[rc].ansr = t[rc].ans = 0;
		t[lc].lazy = t[rc].lazy = t[p].lazy;
		t[lc].li = t[lc].ri = t[rc].li = t[rc].ri = 1;
		t[p].lazy = -1;
	}
}
int query(int p, int l, int r, int x) {
	if (l==r) {
		return l;
	}
	pushdown(p, l, r);
	int mid = l + r >> 1;
	if (t[lc].ans >= x)return query(lc,l, mid, x);
	if (t[lc].ansr + t[rc].ansl >= x)return mid - t[lc].ansr + 1;
	else return query(rc, mid + 1, r, x);
}
void build(int p, int l, int r) {
	t[p] = { 0,0,r - l + 1,r - l + 1,r - l + 1,r - l + 1,-1 };
	if (l == r)return;
	int mid = l + r >> 1;
	build(lc, l, mid);
	build(rc, mid + 1, r);
	pushup(p, l, r);
}
void update(int p, int L, int R, int l, int r, int k) {
	if (L > r || R < l) return;
	if (L <= l && r <= R) {
		t[p].li = t[p].ri = k;
		t[p].ans = t[p].ansl = t[p].ansr = (k ^ 1) * (r - l + 1);
		t[p].lazy = k;
		return;
	}
	pushdown(p, l, r);
	int mid = l + r >> 1;
	update(lc, L, R, l, mid, k);
	update(rc, L, R, mid + 1, r, k);
	pushup(p, l, r);
}
int main() {
	int n, m; cin >> n >> m;
	build(1, 1, n);
	for (int i = 1; i <= m; i++) {
		int op; cin >> op;
		if (op == 1) {
			int x; cin >> x;
			if (t[1].ans >= x) {
				int l = query(1, 1, n, x);
				cout << l << endl;
				update(1, l, l + x - 1, 1, n, 1);
			}
			else {
				cout << 0 << endl;
			}
		}
		else {
			int l, r; cin >> l >> r;
			update(1, l, l + r - 1, 1, n, 0);
		}
	}
	return 0;
}

强烈推荐的一道例题

[COCI2010-2011#6] STEP - 洛谷

  • 26
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值