1 条题解
-
0
题目详细题解
题意简述
给定长度为 的数组 和模数 ,以及一个长度为 的指令字符串 (只包含
'L'和'R')。
按顺序执行指令,每步先输出当前数组所有元素乘积 ,然后:- 若指令为
'L',删除数组最左元素; - 若指令为
'R',删除数组最右元素。
要求输出每一步的余数。
问题难点
直接模拟的时间复杂度为 ,因为每次都需要重新计算乘积( 步 × 每步最多 个元素相乘)。
总和可达 ,因此需要 或 算法。
核心思路
关键观察:
如果按题目顺序(正向)操作,删除元素后乘积会变小,难以直接递推。
但如果反过来思考:从最后一次操作之后(数组为空)开始,逆序执行指令,那么“删除”就变成了“添加”元素,乘积只会乘上新元素,容易维护。具体来说:
- 正向:第 步输出 ,然后删一个元素。
- 逆向:从最后一步(数组只剩 1 个元素)开始,逆序遍历指令,每步“添加”被删除的元素,新乘积 = 旧乘积 × 新元素 。
最终把逆向求出的结果再逆序输出,就是正向答案。
算法步骤
-
确定最后剩下的元素
正向模拟删除过程(不计算乘积,只移动左右指针),找出最后剩下的元素下标。
设初始 ,。遍历前 条指令:- 若 ,则 加 ;
- 若 ,则 减 。
结束后 ,即最后剩下的元素下标。
-
逆向构造答案数组
设 (最后一步的余数)。
然后从 递减到 :- 若 ,说明正向时第 步删了最左元素,那么逆向时我们应添加这个最左元素到当前段左边。
注意:因为正向删除时 递增,逆向添加时 应递减。
所以--l,然后 。 - 若 ,说明正向时第 步删了最右元素,逆向添加时应
++r,然后 。
- 若 ,说明正向时第 步删了最左元素,那么逆向时我们应添加这个最左元素到当前段左边。
-
输出
即为所求。
正确性验证
- 正向:第 步输出时数组为 ,乘积为 。
- 逆向: 表示正向第 步后数组的乘积(即正向第 步输出)。
因为逆向过程正好是添加回正向删除的元素,且乘法模 可交换,所以 正确。
时间复杂度
- 找最后剩余元素:
- 逆向构造 :
- 总复杂度 ,满足要求。
代码注解(对照标程)
int l = 0, r = n - 1; // 正向模拟删除,找出最后剩下的元素下标 for (int i = 0; i < n - 1; i++) { if (s[i] == 'L') l++; else r--; } // 此时 l == r vector<int> b(n); // 最后一步的余数 b[n - 1] = a[l] % m; // 逆向构造 for (int i = n - 2; i >= 0; i--) { if (s[i] == 'L') { // 正向删最左,逆向加回最左 b[i] = (b[i + 1] * a[--l]) % m; } else { // 正向删最右,逆向加回最右 b[i] = (b[i + 1] * a[++r]) % m; } } // 输出 b[0] 到 b[n-1] for (int i = 0; i < n; i++) { cout << b[i] << " "; }
示例验证(以第一组数据为例)
输入:
4 6 3 1 4 2 LRRL-
正向删除过程:
- 初始 ,输出 ,删左 →
- 输出 ,删右 →
- 输出 ,删右 →
- 输出 ,删左 →
-
逆向构造:
- 最后剩 ,
- ,, 从 变 ,
- ,, 从 变 ,
- ,, 从 变 ,
得到 ,与输出一致。
总结
核心技巧:
正向删除难以维护乘积 → 逆向添加,将删除操作转化为乘法操作,利用模运算保持数值范围,实现 解法。 - 若指令为
- 1
信息
- ID
- 6430
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者