1 条题解
-
0
好的,我们先明确题意,然后结合题目例子和常见解法思路给出详细题解。
题目理解
FJ 在时刻 位于 侧面 。
一共有 分钟(时间从 到 ,但注意“第 分钟开始时”的状态会受前一分钟决策影响)。
每分钟开始时,FJ 可以:- 留在当前侧面(不得分)
- 或者 换到对面(并立即获得 分)
此外,有 个硬性要求: 在第 分钟开始时,FJ 必须 在侧面 。
问:在满足所有音频要求的前提下,FJ 最多能获得多少分(即最多“换边”多少次)?
关键约束
- 严格递增,。
- 初始时()在侧面 ,且要求只在 时给出。
- 如果 时刻没有要求,FJ 可以自由选择留在原位或换边(换边即得分+1)。
- 如果有要求, 时刻位置必须等于 。
模型转化
设:
- 表示第 分钟开始时(即从 分钟结束到 分钟开始之间决策后)的位置。
- 初始 。
- 第 分钟开始时的决策:可以选择 (不换边,不得分) 或 (换边,得分+1)。
要求:。
目标:最大化 换边次数。
等价地,我们要求 最少的“停留”次数 来满足位置约束,因为 分钟内最多可能换 次(每分钟都换),但约束会强制某些时刻不能换或必须换。
区间思维
把 看作强制点。
考虑相邻两个强制点 和 ,中间 这段(注意是时间点,不是时刻间隔)。
我们关心:从 出发,经过 次分钟(每次可换或不换),到达 ,最多能换几次?
换边次数限制
设 。
结论:
-
如果 ,即两端相同,那么:
- 最少换边次数(最少得分为)(因为每两次换边回到原位,要满足同侧,必须换偶数次)。
- 最多换边次数为 (如果允许,但必须满足奇偶性约束)。
- 实际可达的换边次数与 同奇偶,且介于 和 之间。
-
如果 ,即两端不同:
- 最少换边次数为 (换奇数次,最小是 ),
- 最多换边次数还是 ,且必须奇数次。
更精确:
$$\{ k \mid k \equiv (b_i \neq b_{i+1}) \pmod{2},\ 0 \le k \le d \} $$
可达的换边次数集合是:但 只在相同且 偶时才可能; 只在不同且 时才可能。
不过我们要 最大化总分,所以我们要尽量选大的 ,但需要保证全局可行。
抽象成区间问题
设 表示第 个强制点后的“累积换边次数”奇偶性相关变量。
实际上,我们可以用 距离 = 时间差 mod 2 来推导约束。
令 表示从起点到第 个强制点(包含)的总换边次数。
那么:
因为每次换边会翻转位置, 就是到此时为止的换边次数。
要求 ,所以:
同时,从 到 :
设 ,则:
- ,其中 。
- 奇偶性:(模 2)。
并且 对应第一个强制点:。
贪心/DP 构造
已知 ,但强制点从 开始。
从 到 ,有 分钟,我们可以任意安排换边次数 (与 同奇偶),且 。然后对于 到 ,,有:
从 可推 ,必须满足 (因为最多每分钟换一次,所以总换边次数不能超过时间)。
最后,最后一个强制点后,从 到 ,还有 分钟,可以继续换边,最大可加分 且奇偶性要匹配最后位置,但注意这最后一段时间没有强制要求,所以可以最大化到 且不必考虑奇偶(因为可以最后一步不换)。
但准确说:最后一段若 到 有 分钟,我们可以选择在每分钟是否换边,没有奇偶约束(因为结束时没有要求),所以最多可以换 次,即最后一段的最大得分就是 。
简化做法
其实这个问题可以简化为:
- 要求 ,且 (因为不能超过时间)。
- 与 同奇偶,并且 。
- 要最大化 (最后一段全换)。
所以关键是最大化 ,同时满足递推。
递推求最大
设 表示第 个强制点时刻 时能达到的最大 。
初始 $F_1 = \max \{ x \mid x \equiv b_1, 0 \le x \le a_1 \}$
即 如果 ,否则 。递推:
且奇偶性必须 。所以 再调整到满足奇偶性(减 0 或 1)。
检查可行性
同时要检查下界:
最小可达 为 吗?等等,不对,因为我们考虑的是 ,所以 不对,因为 可正可负?
不能负,因为 ,即 。所以 必须非递减。
因此 。这很重要:换边次数 不会减少,因为每个 是区间内增加的次数。
所以 序列单调不减。
正确递推
我们有:
并且 。
因此,从 出发,要选一个 且同余 的最大值不超过 。
设 。
可能不满足同余,需要减少到满足同余的最接近值。
所以:
同时要检查 ,如果 ,说明不可能,输出 (但题目保证一定有解?不一定,题目没说无解情况,可能输入保证有解,但我们仍可以判断)。
初始和最终答案
同样由 且同余 得: 若 ,否则 ,但要检查 。
最终答案 = 。
算法步骤
- 读 和要求数组 。
- 对第一个要求:
- ,若 则 。
- 如果 ,则无解(实际不会)。
- 对 从 1 到 :
- 如果 或 或 ,则无解(实际输入保证有解)
- 答案 =
- 输出答案
代码实现
#include <bits/stdc++.h> using namespace std; using ll = long long; void solve() { int n; ll m; cin >> n >> m; vector<ll> a(n); vector<int> b(n); for (int i = 0; i < n; ++i) { cin >> a[i] >> b[i]; } ll f; // 处理第一个要求 if (a[0] % 2 == b[0]) { f = a[0]; } else { f = a[0] - 1; } if (f < 0) f = -1; // 无解标记 for (int i = 0; i < n - 1; ++i) { if (f < 0) break; ll d = a[i+1] - a[i]; ll M = min(a[i+1], f + d); ll nxt = M - ((M - b[i+1]) & 1); if (nxt < f || nxt < 0) { f = -1; break; } f = nxt; } if (f < 0) { // 按照题目保证,这里不会执行 cout << -1 << "\n"; } else { cout << f + (m - a[n-1]) << "\n"; } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) solve(); return 0; }复杂度
每个测试用例 ,总 和 ,轻松通过。
- 1
信息
- ID
- 7279
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者