P11833 [省选联考 2025] 推箱子 题解

题目传送门

思路

首先一个显然的贪心策略:按时间从小到大依次推箱子,而且要一个一个推。证明显然。

接下来我们考虑第 \(i\) 个箱子的可行性。每一个箱子都有三种情况:

  1. \(a_i = b_i\):此时不需要移动;
  2. \(a_i > b_i\):此时第 \(i\) 个箱子需要向左移动;
  3. \(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)\),则可以将其进行数学简化:

\[\begin{aligned} \Delta_{ans} &= \sum_{k = j + 1}^i (b_k - b'_k) \\ &= \sum_{k = j + 1}^i b_k - \sum_{k = j + 1}^i b'_k \\ &= \sum_{k = j + 1}^i [(b_k - k) + k] - \sum_{k = j + 1}^i b'_k \\ &= \sum_{k = j + 1}^i (b_k - k) + \sum_{k = j + 1}^i k - \sum_{k = j + 1}^i b'_k \\ &= \operatorname{sum}(j + 1, i) + \frac{(j + i + 1) \times (i - j)}{2} + \sum_{k = j + 1}^i b'_k\end{aligned} \]

其中 \(\operatorname{sum}(j + 1, i)\) 为线段树中维护的 \(b_i - i\) 之和。注意到 \(b'_k\)\([j + 1, i]\) 中一定是一个连续递增的序列。设 \(len = i - j - 1\) 为移动 \(i\) 所带动的箱子的个数,则最后可以得出:

\[\Delta_{ans} = \operatorname{sum}(j + 1, i) + \frac{(j + i + 1) \times (i - j)}{2} - \frac{(2 \times b_i - len) \times (len + 1)}{2} \]

情况 \(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;
}
posted @ 2026-01-04 16:28  lucasincyber  阅读(3)  评论(0)    收藏  举报