1 条题解
-
0
自动搬运
来自洛谷,原作者为

船酱魔王
如花美眷,似水流年搬运于
2025-08-24 23:15:15,当前版本为作者最后更新于2025-04-12 23:01:19,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题意回顾
- 交互器有一个长度为 的整数序列,初始时已经确定;
- 每次可以选择将一段区间乘 ,交互器输出当前序列的全局最大可以为空的子段和;
- 请用一些操作得到序列每个位置的初始值。
,操作次数限制 次。
分析
- 约定:反转操作意为区间符号反转(乘 ),返回值全称为「全局最大可以为空的子段和的返回值」。
- 注意到对于一个位置连续反转两次相当于不变,因此反转操作具有偶数次抵消性。
- 考虑本题的最简情况:初始时所有数都是正数或负数:
- 当初始时全为非正时,当且仅当反转两次全局后返回 ,那么第一次反转全局的返回值就是整个序列的绝对值和。考虑反转从 开始的后缀,也就是相当于在所有数为非负(第一次全局反转后)的情况基础上,反转了 ,显然此时最大子段和为 开始的后缀的绝对值和,两次返回值的差就是 的值,之后从 开始依次单独反转,相邻两次的返回值差就是每个数的绝对值。
- 当初始时全为非负时(反转一次全局后返回 ),直接用类似的方法即可。此时发现一个性质:若我们将序列所有值转为非负,那么此时的返回值被记录后,可以用来 次操作求出每个位置的值。
- 记录初始状态(两次反转后)返回值 ,此时我们已经询问了 次。此时序列中存在至少一个正数和一个负数。
- 从左到右依次尝试反转每个数(后复原),若在反转一个数 后,返回值大于 ,说明:对于这个序列的最大子段和的任意组成区间,均包含 这个元素。
- 证明:若存在一个区间,其和为序列的最大子段和,不包含 元素,那么它所涉及到的元素均未被修改,为初始状态,则在初始状态下选取这个区间仍能得到大于 的子段和,这是违背前提条件( 为初始状态下的最大子段和)的。
- 考虑优化找到 的过程,可以在反转这个位置时顺便反转(即为复原)上一个位置。而注意找到的 不需要复原。
- 证明一定存在这样的 :该序列中存在正数,因此一定存在一个非空区间和为序列的最大子段和。若把这个区间考虑从左右拓展,然后把遇到的第一个负数变为正数,得到的最大子段和一定大于 ,否则说明这个序列中不存在负数。
- 这步我们消耗了最多 次找到 的查询次数。
- 考虑从 开始往左走,按照在 开始从右到左的顺序尝试反转每个元素:
- 如果反转后返回值变小 / 变大,足以说明这个数的正负。注意当反转后变为负数需要还原(用类似于前文的方法优化次数)。
- 如果反转后返回值不变,而此时它一定处于或与最大子段和区间相邻,那么考虑:如果这个数非 ,那么在为正数时计入这个值一定会让返回值更大;如果两种情况(正负)都计入最大子段和区间,由最大子段和的局部最优性,它左面的区间长度一定一样,那么返回值也会改变。因此这个数必然为 。
- 从 开始向右的操作反之亦然。
- 这步我们消耗了最多 ( 个元素要被反转,两边的元素可能要额外一次还原)次操作。
- 此时,序列中所有元素非负,用 次操作处理完即可。
- 操作次数上限为 次,还是不行。怎么办?
- 注意到卡到上界的条件是翻到最后一个数才最大子段和变大,那么显然从这个数往左往右走时肯定不用往右走,那么不用还原最右面的数,故两步无法同时把次数卡到上界。
- 故我们解决了这一问题。
参考实现
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; const int N = 1005; int T; int n, qmax; int sgn[N]; int a[N]; int ask(int l, int r, int chan = 0) { cout << "? " << l << " " << r << endl; if(!chan) { for(int i = l; i <= r; i++) sgn[i] = -sgn[i]; } cin >> l; return l; } void easy(int x) { for(int i = 1; i <= n - 1; i++) { int tu = ask(i, i, 1); a[i] = x - tu; x = tu; } a[n] = x; cout << "! "; for(int i = 1; i <= n; i++) { cout << a[i] * sgn[i]; if(i < n) cout << " "; else cout << endl; } } int main() { cin >> T; for(int ti = 1; ti <= T; ti++) { cin >> n >> qmax; for(int i = 1; i <= n; i++) sgn[i] = 1; int t1 = ask(1, n); int t2 = ask(1, n); if(t1 == 0 || t2 == 0) { if(t2 == 0) ask(1, n); easy(max(t1, t2)); continue; } int qt = t2; int p = 0; for(int i = 1; i <= n; i++) { int tp = ask(max(i - 1, 1), i); if(tp > qt) { p = i; qt = tp; break; } } if(!p) { cout << "! I AK IOI" << endl; continue; } int oq = 0; for(int i = p - 1; i >= 1; i--) { int tp = ask(i, i + oq); if(tp >= qt) qt = tp, oq = 0; else oq = 1; } if(oq) qt = ask(1, 1); oq = 0; for(int i = p + 1; i <= n; i++) { int tp = ask(i - oq, i); if(tp >= qt) qt = tp, oq = 0; else oq = 1; } if(oq) qt = ask(n, n); easy(qt); } return 0; }
- 1
信息
- ID
- 12002
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者