CF1466I

problem & blog

太神仙了


Algroithm 1

先考虑一下 \(5 \times (n + b)\) 的做法。

假如说我们维护一个栈,来维护已经求出的最大前缀,同时他满足以下条件:

  1. 栈的个数 = 当前维护的最大前缀的位数

  2. 栈中自底向上第 \(k\) 个元素的二进制前 \(k\) 位与当前保留的最大前缀的前 \(k\) 位相同。

然后我们考虑加入元素 \(i\),此时我们在处理第 \(m\) 位。

  • \(i\) 的前 \(m\) 位小于确定的最大前缀,那么直接跳过。

  • \(i\) 的前 \(m\) 位等于确定的最大前缀,那么确定 \(i\) 的第 \(m + 1\) 位,并且把 \(i\) 压入栈中,然后更新最大前缀。

  • \(i\) 的前 \(m\) 位大于确定的最大前缀,那么直接弹出栈顶,然后删掉最大前缀的第 \(m\) 位。

但是我们发现我们用出来的数字不一定是最大值,比如下面这个例子。

\[\bf 10{\color{red}1}0 \]

\[\bf 1{\color{red}0}01 \]

\[\bf {\color{red}1}111 \]

很明显,我们确定出来的前缀是 \(\bf \color{red}101\),但是最底下数的却能提供更大的前缀,\(\bf \color{red}111\)

那么我们如何办呢?

我们从栈顶向下考虑,此时我们在第 \(m\) 位。

  • \(i\) 的前 \(m\) 位等于当前最大值,那么直接 pop 掉这个元素并且更新答案。

  • \(i\) 的前 \(m\) 位大于当前最大值,这就说明最大前缀和从第 \(i + 1\) 位开始一直到第 \(m\) 位,都是假的,然后我们把这些都删除掉即可。

然后我们可以递归处理了。

这个东西需要输出 \(5 \times (n + b)\) 次考虑优化。

Algroithm 2

如果我们得到长度为 \(k\) 的前缀,那么只有堆栈中最低的 \(k\) 个元素可以有这个前缀。其中一些元素在这些位上可能更小,但是在后面提问时,这些问题的回答必定是 \(\bf \text{no}\)。所以我们这一部分并不需要回答。

现在我们的输出量变为 \(4 \times (n + b)\)

Algroithm 3

由于后面不可能在优化了,所以我们考虑前面能否优化。

我们可以让栈顶不一定完全等于最大前缀的前 \(k\) 位,小于等于即可。所以条件

  • 栈中自底向上第 \(k\) 个元素的二进制前 \(k\)等于 当前保留的最大前缀的前 \(k\) 位。

变成

  • 栈中自底向上第 \(k\) 个元素的二进制前 \(k\)小于等于 当前保留的最大前缀的前 \(k\) 位。

并且栈中一定有一个和前缀一样大。

那么为什么这个是对的呢?因为我们发现如果这个数在后面的递归中如果小于保留的前 \(k\) 位那么他必定会删掉。这样子正确性就有保证了。

这样子我们就可以放弃小于的查询了,询问次数变为 \(3 \times (n + b)\)

posted @ 2024-07-05 20:45  sqrtqwq  阅读(1)  评论(0编辑  收藏  举报