P11833 [省选联考 2025] 推箱子 题解
思路
首先一个显然的贪心策略:按时间从小到大依次推箱子,而且要一个一个推。证明显然。
接下来我们考虑第 \(i\) 个箱子的可行性。每一个箱子都有三种情况:
- \(a_i = b_i\):此时不需要移动;
- \(a_i > b_i\):此时第 \(i\) 个箱子需要向左移动;
- \(a_i < b_i\):此时第 \(i\) 个箱子需要向右移动。
情况 1
此时不需要移动,只要判断当前时间是否 \(\le t_i\) 即可。
情况 2
此时,将 \(i\) 从 \(a_i\) 移动到 \(b_i\) 时,还需要将所有被碰到的箱子一起向左推。设 \(j\) 为不会被第 \(i\) 个移动影响的最大下标(\(j < i\))。此时,第 \(j + 1\) 个箱子会被推到 \(a_j < b_i - (i - j - 1)\),移项后即为 \(a_j - j < b_i - i - 1\),其中 \(b_i - i - 1\) 为定值。此时,对于剩下的 \(j + 1 \sim i - 1\) 的箱子,第 \(k\) 个箱子最后会分别移动到 \(b_i - (i - k - 1)\),且移动后 \(j + 1 \sim i\) 为单调递增的序列。那其实我们会得到对于 \(k \in [j + 1, i]\),\(b_k - k\) 应该都是相等的。
情况 3
同情况 \(2\) 可以得到:设 \(j\) 为不会被 \(i\) 个箱子移动影响的最小坐标(\(j > i\))。则可得 \(a_j > b_i + (j - i - 1)\),移项得 \(a_j - j > b_i - i - 1\)。
现在考虑实现。用线段树维护每一个 \(a_i - i\),以及区间和、最小/大值。因为题目中给出 \(a_i < a_{i + 1}\),所以 \(a_i - i \le a_{i + 1} - (i + 1)\),即其具有单调性,也就说明可以二分在 \(\mathcal{O}(\log n)\) 得复杂度内得到情况 \(2,3\) 中的 \(j\) 的下标。同时,只需要在同时维护当前需要花费的最小时间即可。
这里再补充一下每一次是如何维护当前需要花费的最小时间的。对于情况 \(2\),设需移动的区间为 \([j + 1, i]\),我们将答案的增量记作 \(\Delta_{ans} = \sum \limits_{k = j + 1}^i (b_k - b'_k)\),则可以将其进行数学简化:
其中 \(\operatorname{sum}(j + 1, i)\) 为线段树中维护的 \(b_i - i\) 之和。注意到 \(b'_k\) 在 \([j + 1, i]\) 中一定是一个连续递增的序列。设 \(len = i - j - 1\) 为移动 \(i\) 所带动的箱子的个数,则最后可以得出:
情况 \(3\) 同理。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 5, inf = 1e9;
struct node
{
int a, b, t, id;
} c[N];
int tmp, t, n;
struct seg_tree
{
struct node
{
int l, r, sum, maxn, minn, lazy;
} tr[4 * N];
void push_up(int p)
{
tr[p].sum = tr[p * 2].sum + tr[p * 2 + 1].sum;
tr[p].maxn = max(tr[p * 2].maxn, tr[p * 2 + 1].maxn);
tr[p].minn = min(tr[p * 2].minn, tr[p * 2 + 1].minn);
}
void push_down(int p)
{
if (tr[p].lazy != -1)
{
tr[p * 2].lazy = tr[p * 2 + 1].lazy = tr[p].lazy;
tr[p * 2].maxn = tr[p * 2 + 1].maxn = tr[p].lazy;
tr[p * 2].minn = tr[p * 2 + 1].minn = tr[p].lazy;
tr[p * 2].sum = tr[p].lazy * (tr[p * 2].r - tr[p * 2].l + 1);
tr[p * 2 + 1].sum = tr[p].lazy * (tr[p * 2 + 1].r - tr[p * 2 + 1].l + 1);
tr[p].lazy = -1;
}
}
void build(int p, int l, int r)
{
tr[p] = {l, r, 0, 0, inf, -1};
if (l == r)
{
int val = 0;
if (l == 0) val = -inf;
else if (l == n + 1) val = inf;
else val = c[l].a - l;
tr[p].sum = tr[p].maxn = tr[p].minn = val;
return;
}
int mid = (l + r) >> 1;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
push_up(p);
}
void modify(int p, int l, int r, int x)
{
if (l <= tr[p].l && tr[p].r <= r)
{
tr[p].lazy = tr[p].minn = tr[p].maxn = x;
tr[p].sum = x * (tr[p].r - tr[p].l + 1);
return;
}
push_down(p);
int mid = (tr[p].l + tr[p].r) >> 1;
if (l <= mid) modify(p * 2, l, r, x);
if (r > mid) modify(p * 2 + 1, l, r, x);
push_up(p);
}
int queryL(int p, int l, int r, int x)
{
if (l == r) return l;
push_down(p);
int mid = (tr[p].l + tr[p].r) >> 1;
if (tr[p * 2 + 1].minn >= x) return queryL(p * 2, l, mid, x);
return queryL(p * 2 + 1, mid + 1, r, x);
}
int queryR(int p, int l, int r, int x)
{
if (l == r) return l;
push_down(p);
int mid = (tr[p].l + tr[p].r) >> 1;
if (tr[p * 2].maxn > x) return queryR(p * 2, l, mid, x);
return queryR(p * 2 + 1, mid + 1, r, x);
}
int query(int p, int l, int r)
{
if (l <= tr[p].l && tr[p].r <= r) return tr[p].sum;
push_down(p);
int mid = (tr[p].l + tr[p].r) >> 1, res = 0;
if (l <= mid) res += query(p * 2, l, r);
if (r > mid) res += query(p * 2 + 1, l, r);
return res;
}
} ST;
bool cmp(node x, node y)
{
return x.t < y.t;
}
void solve()
{
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
{
scanf("%lld%lld%lld", &c[i].a, &c[i].b, &c[i].t);
c[i].id = i;
}
ST.build(1, 0, n + 1);
sort(c + 1, c + n + 1, cmp);
int nowt = 0;
for (int i = 1; i <= n; i++)
{
int pos = ST.query(1, c[i].id, c[i].id) + c[i].id;
if (pos == c[i].b)
{
if (nowt > c[i].t)
{
printf("No\n");
return;
}
}
else if (pos > c[i].b)
{
int j = ST.queryL(1, 0, n + 1, c[i].b - c[i].id + 1), len = c[i].id - j - 1;
int now = ST.query(1, j + 1, c[i].id) + (j + c[i].id + 1) * (c[i].id - j) / 2 - (2 * c[i].b - len) * (len + 1) / 2;
nowt += now;
if (nowt > c[i].t)
{
printf("No\n");
return;
}
ST.modify(1, j + 1, c[i].id, c[i].b - c[i].id);
}
else
{
int j = ST.queryR(1, 0, n + 1, c[i].b - c[i].id - 1), len = j - c[i].id - 1;
int now = (2 * c[i].b + len) * (len + 1) / 2 - ST.query(1, c[i].id, j - 1) - (j + c[i].id - 1) * (j - c[i].id) / 2;
nowt += now;
if (nowt > c[i].t)
{
printf("No\n");
return;
}
ST.modify(1, c[i].id, j - 1, c[i].b - c[i].id);
}
}
printf("Yes\n");
}
signed main()
{
scanf("%lld%lld", &tmp, &t);
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号