【NOI Online 2022 提高组 B】【洛谷 P8252】讨论 题解

一口气写了 2700 多字,然而代码只有 1500 B(

Description

传送门

Solution

显然,我们所要做的事,就是构造出一对 ( i , j ) (i,j) (i,j),使得编号为 i , j i,j i,j 的人会讨论。

算法一

考虑枚举位置 p p p,接着再枚举包含位置 p p p ( S i , S j ) (S_i,S_j) (Si,Sj)。这样一来,条件 S i ∩ S j S_i \cap S_j SiSj 就得到了满足,接下来还要判定 S i , S j S_i,S_j Si,Sj 是否存在互相包含的关系。

不妨设 ∣ S i ∣ ≤ ∣ S j ∣ |S_i| \le |S_j| SiSj。那么不难得到下面的判定方法: 开一个桶,先对于每个属于 S j S_j Sj 的位置 p p p 在桶中打上一个标记。接着,我们再扫描 S i S_i Si 中的每一个位置 q q q,若桶中的第 q q q 位上没有标记,那么 S i , S j S_i,S_j Si,Sj 就不存在互相包含的关系。反之,若对于每个 q q q 都有标记,那么 S i ∈ S j S_i \in S_j SiSj,存在互相包含的关系。

于是我们得到了一个暴力做法。接下来考虑正解。

算法二

根据算法一的启示,我们考虑将各个 S S S 先从大到小排序,然后再依次扫描并维护标记数组 v v v。初始时 v v v 中所有元素均为 0 0 0

考虑下面的贪心算法:

  • 假设目前扫描到了 S i S_i Si
  • ∀ x ∈ S i \forall x \in S_i xSi,若 v x v_x vx 全部相等,那么我们将这些 v x v_x vx 全部改为 i i i。否则,我们找到所有满足 v x ≠ 0 v_x \neq 0 vx=0 x x x,选出其中满足 ∣ S x ∣ |S_x| Sx 最小的 x x x,那么就得到了一组答案 ( S x , S i ) (S_x,S_i) (Sx,Si)
  • 若经过上述流程始终没有找到任何一组答案,则输出 NO

本算法来自 ix35 聚聚,这里献上对祂虔诚的膜拜。不过祂并没有写证明以及是如何想到了,于是我来补一个证明及思维导航叭。

Prove

首先,该算法找到的答案必然满足题面所述的条件。所以,下面我们需要只证明,如果答案不是 NO,那么一定能找到答案就相当于证明了算法的正确性

考虑使用归纳法予以证明。对于 n = 2 n=2 n=2 的情况,上述做法显然正确。下面,我们将尝试证明算法于 n = k n=k n=k 时正确的前提下,在 n = k + 1 n=k+1 n=k+1 时依然正确。

我们假设在 n = k n=k n=k 时并没有找到答案,但在加入一个集合 S n + 1 S_{n+1} Sn+1 补在原先的 n n n 个集合之后的时候首次存在了满足要求的数对。

假设能与 n + 1 n+1 n+1 共同构成合法数对的众多集合中最靠后(即size 最小)的集合为 S j ( j ≤ n ) S_j(j \le n) Sj(jn)。那么,我们需要证明, S j S_j Sj 会被该算法找到。

首先,一个很明显的性质是,由于加入 S j S_j Sj 的时候没有找到合法的数对,所以在处理完 S j S_j Sj 之后, ∀ x ∈ S j \forall x \in S_j xSj p x p_x px 均被赋值为了 j j j。注意到原算法中是从桶中拉出来一个数与当前的集合编号组成了数对,所以我们需要证明,在扫描完 ( j , n ] (j,n] (j,n] 过后, ∀ x ∈ { S j ∪ S n + 1 } \forall x \in \{S_j \cup S_{n+1}\} x{SjSn+1} p x p_x px 中至少有一个等于 j j j

考虑反证法。假设在扫描完 ( j , n ] (j,n] (j,n] 后, ∀ x ∈ { S j ∪ S n + 1 } \forall x \in \{S_j \cup S_{n+1}\} x{SjSn+1} p x p_x px 均已被改动为一个不等于 j j j 的数。

由于 S j S_j Sj S n + 1 S_{n+1} Sn+1 有交,所以 ( j , n ] (j,n] (j,n] 中必然存在一个 S i S_i Si 使得 S i ∪ S n + 1 ≠ ∅ ,   S i ∪ S j ≠ ∅ S_i \cup S_{n+1} \neq ∅,\ S_i \cup S_j \neq ∅ SiSn+1=, SiSj= 有交,否则不可能覆盖掉任何同时属于 S j S_j Sj S n + 1 S_{n+1} Sn+1 的位置。

注意到 j < i < n + 1 j<i<n+1 j<i<n+1,根据前面的假设, S j S_j Sj 是与 S n + 1 S_{n+1} Sn+1 构成合法集合对的 size 最小的集合,这说明了 S i S_i Si S n + 1 S_{n+1} Sn+1 无法构成合法的集合对。然而 S i S_i Si S n + 1 S_{n+1} Sn+1 有交,所以 S i S_i Si 必须完全包含 S n + 1 S_{n+1} Sn+1

  • 因为 S n + 1 S_{n+1} Sn+1 S j S_j Sj 有交, S n + 1 S_{n+1} Sn+1 中的每个元素都属于 S i S_i Si,所以 S j S_j Sj S i S_i Si 有交。
  • 因为 S n + 1 S_{n+1} Sn+1 中有不属于 S j S_j Sj 的元素, S n + 1 S_{n+1} Sn+1 中的每个元素都属于 S i S_i Si,所以 S i S_i Si 中存在不属于 S j S_j Sj 的元素。
  • 因为 S j S_j Sj 中存在不属于 S i S_i Si 的元素,所以 ∣ S j ∣ ≠ ∣ S i ∣ |S_j| \neq |S_i| Sj=Si,更进一步的有 ∣ S j ∣ > ∣ S i ∣ |S_j|>|S_i| Sj>Si。所以 S j S_j Sj 中有不属于 S i S_i Si 的元素。

综合上述三条,可以得到 S i , S j S_i,S_j Si,Sj 构成合法数对,而这与最开始的假定( S j S_j Sjsize 最小的能与 S n + 1 S_{n+1} Sn+1 构成合法集合对的集合)相矛盾。

综上所述,假设不成立。本算法正确。

关于这个算法是如何想到的

首先,暴力做法(算法一)非常显然,至少我是很轻松就想到了。

算法一给了我们一个十分关键的启示,那就是开桶维护,并且先扫描 size 较大的并在桶里打标记,再扫描 size 较小的在桶里面扫描找答案。这是通向正解大门的第一步。

于是我们就想到了排序。我们还要在排序的过程中维护一个桶。不过,当前扫描过的那么多集合中,覆盖同一个位置的集合可能有很多,所以我们到底该在桶里面打上什么样的标记呢?

不难想到,我们要记录覆盖这个位置的,并且最有可能和后面的集合构成合法集合对的集合编号。而至于哪个集合最优可能与其他集合构成答案,这需要从第二个步骤,即扫描桶找答案这一步进行思考。

假设桶里面维护的真的是最优秀的集合,那么我们只需要扫描桶里面的对应位置,判断它们是否相等就好了。如果不相等就找到了答案,但是如果相等呢?这时我们就要不得不做出一个决策,究竟是将当前的集合编号丢进去把原先的替代掉,还是保留原来的不动?

不难发现原来的集合 S S S 完全包含了当前扫描到的集合 T T T,当接下来新加入一个集合的话,如果它和 S S S 构成了合法集合对,那么也必然会和 T T T 构成合法集合对,当然是这个新加入进来的集合与 T T T 有交的前提下。于是,我们便可以枚举 x ∈ T x \in T xT,然后把桶里面的第 x x x 个位置给替换成当前正在处理的集合的编号了。

于是我们得到了一个可以感性理解其正确性的做法。接着我们就会通过一组数据把它给 Hack 掉:

1
3
9 1 2 3 4 5 6 7 8 9
6 4 5 6 7 8 9
3 3 4 5

然后我们就能进一步改进这个贪心,每次选择集合的 size 最小的集合以保证能够组成合法集合对了。接着为了防止思路再一次出问题,我们就会尝试给出一个较为严谨证明。上面已经叙述过了。

该如何评价这道题呢?它不需要什么码力,是一道纯思维题。做这种题需要套路的积累吗?不得不说,需要,但是需要得不多,这道题目就考察你敏锐的洞察力,以及从暴力做法中剥丝抽茧得到暴力做法的本质,并尝试将其套用到正解上的水平。此外,这种题目还要敢想敢做,谁说桶就不能维护多个集合的信息了?其实,从本质上来讲,本题中的桶之所以能够维护多个集合的信息,就是因为如果集合 S S S 完全包含了 T T T,那么在桶的那些属于 T T T 位置就没有保留的必要了。

好多时候,这种集合最优包含类的题目都是贪心(其实大多数是网络流,二分图 )。也是一种不错的选择。以后应该知道该我往什么方面思考了吧——对,greedy yyds。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值