冬令营前交互专练

冬令营前交互专练

P13782 [eJOI 2022] Where Is the Root?

有点唐。发现交互次数是严格 \(\log n\),启发我们想到二分答案。我们做一些基本的分类讨论:

  • 假设根节点不为叶子节点,如果我们每次都询问所有叶子节点,那么只有在询问集合中有根节点的时候才会回答 YES
  • 假设根节点为叶子节点,如果我们逐个加入每个叶子节点,那么只有在询问集合中有根节点的时候才会回答 YES

结合上面的两个分析,我们可以得出一个做法:将所有点按照度数排序,然后二分答案,check 的方式是询问 \(1\sim mid\) 中所有点,看回答是不是 YES,如果是说明根节点在 \([l,mid]\),否则在 \((mid,r]\)。注意到此时如果 \(mid=1\) 会有问题,但是题目中保证了至少有三个叶子,所以我们问一下第一个点和第三个点即可。这样的话我们的操作次数是严格 \(\log n\) 的,可以通过。

P5208 [WC2019] I 君的商店

观察数据范围,先来看子任务三,这个包显然需要特殊处理。此时我们的序列一定单调不减或者单调不增,可以通过询问 \(a_0,a_{n-1}\) 的大小关系判断是哪一种,以单调不减为例。

我们考虑二分这个中间的分割点,现在我们需要进行 check。考虑我们知道的信息有什么,很显然只有 \(a_{n-1}=1\),考虑能不能通过 \(1\) 询问出中间两个元素的信息。我们假设中间的元素是 \(x,y\),先花一次询问令 \(x\le y\),然后我们可以发现:

  • 如果 \(x+y\le 1\),则 \(x=0\)
  • 如果 \(x+y\ge 1\),则 \(y=1\)

那么我们一定可以确定中间两个元素中某一个的值,然后依据这个转移二分区间即可。但是注意最后 \(l=r\) 的时候我们无法确定 \(a_l\) 的值,因此可以用奇偶性来判断。这样单次代价为 \(5\),总代价 \(5\log n\)

然后我们考虑剩下的部分,题目的要求次数是 \(7n+5\log n\to 5n+5\log n\),我们先考虑怎么做到 \(7n\)。通过上面的过程可以知道我们在知道一个 \(1\) 之后可以花 \(5\) 的代价问出一个元素的具体值,那么我们的目标是 \(2n\) 次操作求出一个 \(1\)。这个其实非常简单,因为 \(1\) 就是区间中的最大值,我们扫一遍数组每次花 \(2\) 的代价比较一下更新最大值就行了,总代价为 \(7n\)。注意最后依然会剩下一个元素无法确定,用奇偶性判断即可。

最后考虑结合上面的两个做法。此时我们的要求是 \(5n+5\log n\),因此我们要在 \(5n\) 的代价求出一个递增序列,然后用子任务三的做法二分求解递增序列即可。考虑上面的做法中我们花了额外的 \(2n\) 次操作求最大值,我们考虑如果不知道最大值的话怎么询问。我们将 \(1\) 换成一个变量 \(z\),然后可以发现:

  • 如果 \(x+y\le z\),则 \(x=0\)
  • 如果 \(x+y\ge z\),则 \(z \le y\)

那么在花费 \(5n\) 的代价后,我们可以得到一个递增序列以及一些为 \(0\) 的元素;需要注意最后一次操作的时候我们会确定一个 \(1\),并且我们还有一个元素 \(p\) 无法处理。接下来利用这个 \(1\) 在递增序列上进行二分,二分之后我们还有一个元素 \(q\) 无法确定。此时我们用 \(1\) 再问一遍 \(p,q\) 的信息可以确定一个元素,这样最后只有一个元素无法确定,利用奇偶性进行调整即可。这样的话可以做到 \(5n+5 \log n\) 了。

P5473 [NOI2019] I 君的探险

下文用 \((a,b,c)\) 表示该算法 modifyquerycheck 的次数。

Subtask 1~5

我们对于 \(i\in [1,n]\),每次改变 \(i\) 的状态,然后此时查询哪些点的状态改变了,这些点就和 \(i\) 有直接连边。

注意到我们只用考虑 \(>i\) 的点,并且最后一个点不需要考虑,因此可以做到 \((n-1,\frac{n(n-1)}{2},0)\)

Subtask 6~9

这是一个比较关键的包,这个包的做法将决定这个题之后的思考路径。观察要求得知我们的复杂度应该是 \((n\log n,n\log n,0)\) 的,这启发我们进行如下思考:

  • 考虑对于一条边 \((x,y)(x<y)\),我们在 \(y\) 处计算答案。此时我们考虑在 \([1,y)\) 这个序列上做二分,显然只有当点亮的区间包含 \(x\) 的时候 \(y\) 才会被点亮,于是单次我们可以做到 \(O(n\log n)\)

    对于 \(n\) 个点的情况,考虑整体二分。我们每次点亮 \([l,mid]\) 中的点,然后看那些点被点亮了,将他们扔到左区间递归,剩下的扔到右区间。这样可以做到上述复杂度。

  • 换一种思路,这个亮灯操作非常像异或,考虑按位操作。我们枚举二进制位,然后点亮当前位为 \(1\) 的所有灯。那么发现我们实际上可以得出每个灯和它所有相邻灯的编号异或和。这样可以轻松做到上述复杂度。

Subtask 10~11

显然可以套用整体二分的做法做到同样的复杂度。其实这两个子任务是本质相同的,保证了每一个点的相邻节点只有一个比自己小。

Subtask 12~17

这个包我们需要做到 \((n\log n,n\log n,2n)\) 的复杂度,但是常数可以大一点。考虑利用按位处理的方法,现在的问题是我们需要知道哪些点是叶子,这样我们可以通过剥叶子的方式求出每一条边。

考虑到叶子的特点是只有一个出边,因此叶子的异或和 \(S_i\) 就是他唯一的一个相邻节点。考虑对于 \(i\in [1,n]\),每次只点亮 \(S_i\),然后此时如果 \(i\) 的状态改变了就报告边 \((i,S_i)\),最后 check 一下 \(i\),这样我们就可以知道哪些点是叶子了。

Subtask 18~25

最后来到正解,我们依然需要做到 \((n\log n,n\log n,2n)\) 的复杂度。但是对于任意图我们似乎没有任何入手点了。

考虑到交互库是非自适应的,因此考虑随机化。先回顾上面的整体二分做法:我们每次点亮 \([l,mid]\),那么此时被点亮的点一定和这个区间内的点有连边,而没有被点亮的不一定没有,但是我们可以认为他没有。我们只需要随机若干个排列 \(p_i\),每次点亮 \(p_{[l,mid]}\) 这些点判断,这样我们每次二分都能确定一些边。在随机条件下我们认为这样可以随出所有的边。

但是此时还是过不去,因为我们还没有用 check 函数。考虑每次二分完对每个点进行 check,将没有用的点扔掉,这样可以大大减少前面修改和查询的常数。这样的话就可以通过这道题。

需要注意的点是我们在二分的时候需要排除掉已经确定的边的影响,可以把当前确定的图存下来然后暴力判断。并且需要注意二分的时候修改查询的常数,否则有可能跑不过去。

posted @ 2026-01-31 18:31  UKE_Automation  阅读(35)  评论(0)    收藏  举报