题解:P2894 [USACO08FEB] Hotel G

· · 题解

一道不错的线段树入门题。

简略题意:m 个查询:查询 L 最小的 [L,L+x-1] 连续 0 段,若存在则这段变为 1;操作把 [x,x+y-1] 段变为 0

我们先考虑如何判断是否存在连续的 x 长空段。于是自然地想到线段树维护连续段,当根节点的最大连续段不小于 x 有解。

接下来考虑如何找到答案。为了保证答案尽量小,我们从左至右地依次讨论:

如此的,我们便可唯一确定最小答案。

对于修改操作,我们只需维护三个值:

这样,便可维护整个序列。 ```cpp struct SegmenTree { struct node { int l, r; int lmax, rmax, smax; int lazy; int siz () { return r - l + 1; } } t[MAXN << 2]; #define ls (x << 1) #define rs (x << 1 | 1) void upd (int x, int k) { t[x].lazy = k, t[x].lmax = t[x].rmax = t[x].smax = t[x].siz () * (k ^ 1); } void pushup (int x) { t[x].lmax = (t[ls].lmax == t[ls].siz () ? t[ls].lmax + t[rs].lmax : t[ls].lmax), t[x].rmax = (t[rs].rmax == t[rs].siz () ? t[rs].rmax + t[ls].rmax : t[rs].rmax); t[x].smax = max ({t[ls].smax, t[rs].smax, t[ls].rmax + t[rs].lmax}); } void pushdown (int x) { if (t[x].lazy != -1) upd (ls, t[x].lazy), upd (rs, t[x].lazy); t[x].lazy = -1; } void build (int x, int l, int r) { t[x].l = l, t[x].r = r; t[x].lmax = t[x].rmax = t[x].smax = 0; t[x].lazy = -1; if (l == r) return t[x].rmax = t[x].lmax = t[x].smax = 1, void (); int mid = l + r >> 1; build (ls, l, mid); build (rs, mid + 1, r); pushup (x); } int query (int x, int l, int r, int len) { int mid = l + r >> 1; pushdown (x); if (t[ls].smax >= len) return query (ls, l, mid, len); else if (t[ls].rmax + t[rs].lmax >= len) return mid - t[ls].rmax + 1; // 左子树右端点最大连续段起点即为答案 else if (t[rs].smax >= len) return query (rs, mid + 1, r, len); else return 0; } void modify (int x, int l, int r, int k) { if (t[x].l > r || l > t[x].r) return; if (t[x].l >= l && t[x].r <= r) return upd (x, k); pushdown (x); modify (ls, l, r, k); modify (rs, l, r, k); pushup (x); } } t; ``` 结语:这题与 [P4513](https://www.luogu.com.cn/problem/P4513) 其实很像,所采用的思想都一致。这题的关键在于想到这个 Trick 是否能进一步思考下去,进而写出正确的 query 函数。