【題解】P2894 - [USACO08FEB] Hotel G
題目大意
給定一個長度為 \(n\) 的序列和 \(m\) 個操作,初始時序列中的 \(n\) 個元素都是空房。每次操作有兩種格式:
-
1 x
,表示詢問序列中是否存在連續 \(x\) 個空房。若存在,輸出這些空房中最左端空房的編號。如果有多個區間滿足條件,按最左端的區間處理。輸出編號後在選出的 \(x\) 個連續空房內住人。 -
2 x y
,表示令區間 \([x, x + y - 1]\) 內的客人退房。
\(1 \leq n, m \leq 5 \times 10^4\)
解題思路
首先分析題目中的關鍵詞“連續”。其實對於是否存在 \(x\) 個連續空房的操作而言,我們只需要知道最大連續空房數 \(k\)
我們需要維護一個 支援查詢最大子段和 和 給區間統一賦值 的線段樹。查詢最大子段和的方法可以參考 P4513。我們對於每一個區間 \([l, r]\)
如果必須包含左端點,那麼最大子段和有兩種情況。第一,直接 \(lsum_l\) 轉移,即包含最大子段和的區間被左子區間完全覆蓋。第二,\(lsum_l = sum_l + lsum_r\),即包含最大子段和的區間左子結點為左半區間的左端點,右端點從左半區間延伸到了右半區間。此時左半區間被完全覆蓋,右半區間最好的情況為 \(lsum_r\)
整個區間的最大子段和要麼被左半區間或右半區間直接貢獻,要麼左半區間和右半區間連線後產生了更優的最大子段和。第一種情況的貢獻為 \(\max(val_l, val_r)\),第二種情況只有可能發生在整個區間的中間位置,否則最大子段和不滿足連續。換言之,新產生的最大子段和應該是由左半區間的右邊一部分與右半區間的左邊一部分連線而成的,否則貢獻最大子段和的區間就會斷開。顯然第二種情況的貢獻為 \(rsum_l + lsum_r\),否則類似上面的反證,存在更優值的情況與定義矛盾,所以 \(rsum_l + lsum_r\) 一定是第二種情況的最優貢獻。兩者取較大值就是整個區間的最大子段和,\(val = \max(val_l, val_r, rsum_l + lsum_r)\)。
對於區間整體賦值的維護十分簡單,多維護一個 \(lazy\) 標記表示當前區間被賦的值即可,這裡不再贅述。
至此,這道筆者 \(5\) 月份竟然不會做的題就簡單地解決了。是不是非常簡單呢?我也是這樣想的(惱。來,試試看。
參考程式碼
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 5e4 + 5;
struct node
{
int l, r;
long long lsum, rsum;
long long lazy, sum, val;
} tree[maxn << 2];
int n, m;
void push_up(int k)
{
tree[k].sum = tree[2 * k].sum + tree[2 * k + 1].sum;
tree[k].lsum = max(tree[2 * k].lsum, tree[2 * k].sum + tree[2 * k + 1].lsum);
tree[k].rsum = max(tree[2 * k + 1].rsum, tree[2 * k + 1].sum + tree[2 * k].rsum);
tree[k].val = max(max(tree[2 * k].val, tree[2 * k + 1].val), tree[2 * k].rsum + tree[2 * k + 1].lsum);
}
void push_down(int k)
{
if (tree[k].lazy != 0)
{
if (tree[k].l == tree[k].r)
{
tree[k].lazy = 0;
return;
}
tree[2 * k].sum = (tree[2 * k].r - tree[2 * k].l + 1) * tree[k].lazy;
tree[2 * k + 1].sum = (tree[2 * k + 1].r - tree[2 * k + 1].l + 1) * tree[k].lazy;
if (tree[k].lazy > 0)
{
tree[2 * k].lsum = tree[2 * k].rsum = tree[2 * k].val = (tree[2 * k].r - tree[2 * k].l + 1) * tree[k].lazy;
tree[2 * k + 1].lsum = tree[2 * k + 1].rsum = tree[2 * k + 1].val = (tree[2 * k + 1].r - tree[2 * k + 1].l + 1) * tree[k].lazy;
}
else
{
tree[2 * k].lsum = tree[2 * k].rsum = tree[2 * k].val = tree[k].lazy;
tree[2 * k + 1].lsum = tree[2 * k + 1].rsum = tree[2 * k + 1].val = tree[k].lazy;
}
tree[2 * k].lazy = tree[2 * k + 1].lazy = tree[k].lazy;
tree[k].lazy = 0;
}
}
void build(int k, int l, int r)
{
tree[k].l = l;
tree[k].r = r;
if (l == r)
{
tree[k].sum = tree[k].lsum = tree[k].rsum = tree[k].val = 1;
return;
}
int mid = (l + r) / 2;
build(2 * k, l, mid);
build(2 * k + 1, mid + 1, r);
push_up(k);
}
void update(int k, int l, int r, long long w)
{
if (tree[k].l >= l && tree[k].r <= r)
{
tree[k].sum = (tree[k].r - tree[k].l + 1) * w;
if (w > 0)
tree[k].lsum = tree[k].rsum = tree[k].val = (tree[k].r - tree[k].l + 1) * w;
else
tree[k].lsum = tree[k].rsum = tree[k].val = w;
tree[k].lazy = w;
return;
}
push_down(k);
int mid = (tree[k].l + tree[k].r) / 2;
if (l <= mid)
update(2 * k, l, r, w);
if (r > mid)
update(2 * k + 1, l, r, w);
push_up(k);
}
node query_sum(int k, int l, int r)
{
if (tree[k].l >= l && tree[k].r <= r)
return tree[k];
int mid = (tree[k].l + tree[k].r) / 2;
push_down(k);
if (r <= mid)
return query_sum(2 * k, l, r);
if (l > mid)
return query_sum(2 * k + 1, l, r);
else
{
node ret;
node lson = query_sum(2 * k, l, mid);
node rson = query_sum(2 * k + 1, mid + 1, r);
ret.sum = lson.sum + rson.sum;
ret.lsum = max(lson.lsum, lson.sum + rson.lsum);
ret.rsum = max(rson.rsum, rson.sum + lson.rsum);
ret.val = max(max(lson.val, rson.val), lson.rsum + rson.lsum);
return ret;
}
}
int query_idx(int k, long long w)
{
if (tree[k].l == tree[k].r)
return tree[k].l;
push_down(k);
if (tree[2 * k].val >= w)
return query_idx(2 * k, w);
else if (tree[2 * k].rsum + tree[2 * k + 1].lsum >= w)
return tree[2 * k].r - tree[2 * k].rsum + 1;
else if (tree[2 * k + 1].val >= w)
return query_idx(2 * k + 1, w);
}
int main()
{
int opt, x, y;
scanf("%d%d", &n, &m);
build(1, 1, n);
for (int i = 1; i <= m; i++)
{
scanf("%d", &opt);
if (opt == 1)
{
scanf("%d", &x);
long long val = query_sum(1, 1, n).val;
if (val >= x)
{
int idx = query_idx(1, x);
update(1, idx, idx + x - 1, -maxn);
printf("%d\n", idx);
}
else
puts("0");
}
else
{
scanf("%d%d", &x, &y);
update(1, x, x + y - 1, 1);
}
}
return 0;
}