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

irris
Good Luck.搬运于
2025-08-24 22:53:02,当前版本为作者最后更新于2023-12-09 20:29:47,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
有人觉得 Hard Ver 比 Easy Ver 简单,哎。
设上一次询问的集合 。我们使用 表示 内元素的乘积。同时,考虑维护 的符号。
我们维护 ,这是一个看起来很自然的想法,因为我们就可以比较少的减小 对后续状态的影响。
根据 Easy Ver 的思路,施二分 。考虑把当前区间集合分成两半 。首先,令 。如果 改变了符号,那么此时一定有 的符号是确定的。令 后继续找到相应的一部分二分即可。
否则, 没有改变符号,这说明一定有 与原来的 同号,我们已经确定了负号存在于 或 中。因为这个时候 $\frac{\lang S\rang \cdot \lang A \rang}{\lvert Q\rvert}$ 其实也不是很大,那么我们由于仿照上例需要给 的符号改变方便后续维护,所以令 ,这样 的符号就变化掉了,我们最多实行两次 就可以把 的符号变掉,这样同样形成了易于维护的形式。
朴素实现是 $S_{\max} \leq n + \lceil \frac n2 \rceil + \lceil \frac n4 \rceil + \cdots \leq 2n + \mathcal O(\log_2 n)$ 的,但是注意到我们可以先行询问一次 来找到负号的位置,所以就变成了 $\frac n2 + 2(\frac n2 + \mathcal O(\log_2 n)) = 1.5n + \mathcal O(\log_2 n)$,非常优秀。
实际上没有卡交互次数,反正都是期望 ,就直接保证负号位置随机了。
Subtask 0 特判乘积为 后二分,Subtask 1 是古老做法被 srz 一拳爆了(想要看的可以私聊我),Subtask 2 是增加区分度用的。
std::vector<int> S; inline char query(int l = 1, int r = 0) { std::cout << "? " << S.size() + (r - l + 1) << ' '; for (int i : S) std::cout << i << ' '; for (int i = l; i <= r; ++i) std::cout << i << ' '; std::cout << std::endl; char x; std::cin >> x; return x; } inline void pb(int l, int r) { for (int i = l; i <= r; ++i) S.push_back(i); } inline char reverse(char ch) { return ch == '+' ? '-' : ch == '-' ? '+' : ch; } inline char func(char ch1, char ch2) { return ch1 == '0' ? ch2 : ch1; } void solve() { int N, K, Smax; std::cin >> N >> K >> Smax; S.clear(); if (N == 1) { std::cout << "! 1" << std::endl; return; } pb(1, pos[N]); char lst = query(), Ssgn = lst; int l = 1, r = N; if (lst == '+') l = pos[N] + 1; else r = pos[N]; while (l < r) { int mid = l + r >> 1; char now = query(l, mid); if (now != lst) { char Nsgn = func(reverse(lst), now); pb(l, mid); if (Ssgn == Nsgn) l = mid + 1; else r = mid, Ssgn = Nsgn; } else { char Asgn = lst == Ssgn ? '+' : '-', orig = now; pb(l, mid), pb(mid + 1, r), Ssgn = reverse(Ssgn); while ((now = query()) == orig); if (Asgn == '+') l = mid + 1; else r = mid; } lst = now; } std::cout << "! " << l << std::endl; }其中 是提前 预处理出来的最优下标,可以保证通过 Subtask,不过反正我数据里 是一定范围和一定概率随的, 是 shuffle 的,所以似乎并不很有必要。
- 1
信息
- ID
- 8882
- 时间
- 1800ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者