P11983 [JOIST 2025] 展览会 3 / Exhibition 3

P11983 [JOIST 2025] 展览会 3 / Exhibition 3

感觉还算有迹可循,搜索树并不算大。

因此有的步骤可以钦定自己前面做的是对的,以及猜猜结论。

Part 1

如果 \(a\) 中每个颜色只出现一次,怎么做?

从前往后考虑每个区间,每次尽量找大的颜色填进去。

你考虑一个选颜色的方案合法的充要条件。如果对于每个颜色 \(c\),所有颜色为 \(c\) 的区间的交集不为空,这个方案就是合法的。

为了快速查找最大的 \(c\),自然地想到把每个颜色 \(c\) 的交集放到线段树上。不妨记作 \([L_c,R_c]\)

还发现 \([L_c,R_c]\) 一定是两两不交的。

扫描所有区间,并维护 \([L_c,R_c]\) 即可做到 \(O(m \log n)\)


Part 2

如果有的颜色出现多次,怎么做?

依然考虑充要条件。如果对于每个颜色 \(c\),所有颜色为 \(c\) 的区间可以用不超过 \(cnt(c)\) 个点覆盖,这个方案就是合法的。

这个看上去就比 \(Part 1\) 难刻画多了。快速判定、快速查询都不好做了。

首先有个显而易见的事实:如果有一种颜色 \(c\),选择 \(c\) 的区间不超过 \(c\) 个,那么一定可以往颜色 \(c\) 继续加入区间

然后发现,每个时刻,这样的颜色 \(c\) 只有一个是需要考虑的。

先想想判定。这是一个经典的问题,把所有区间按 \(l\) 排序,从前往后放点,每个点尽量靠右放。最后 check 是否用了超过 \(cnt(c)\) 个点。

你发现另一个显而易见的事实:如果用的点数 \(<cnt(c)\),一定可以往颜色 \(c\) 加区间

仔细思考,由于只有 \(= cnt(c)\) 是需要精细处理的,考虑找到一个时刻 \(t\),满足如果将 \(t\) 前剩余的所有区间加入进去,恰好需要用 \(cnt(c)\) 个点去覆盖这些区间。

一次 \(check\)\(O(len \log len)\) 的。直接二分这个时刻 \(t\),复杂度会退化到 \(O(m^2 log^2 m)\)

由于 \(check\) 复杂度跟 \(len\) 相关,可以想到使用 \(\text {finger search}\),先倍增 \(len\),直到使用的点数 \(\geq cnt(c)\) 时再跑二分,复杂度就是 \(O(\sum_i (len_i \log^2 len_i)) = O(m \log^2 m)\)


Part 3

\(\text{finger search}\) 这块拼图看上去很有道理。

接下来我们还要做这样一件事:

有一些颜色 \(c\),每种颜色都满足 \(\text{mincover}(c) = cnt(c)\),对于一个区间 \([l,r]\),我们要快速查询其能选择的最大颜色 \(c\),或者都不能加入。

如果能 \(polylog(n)\) 做这个,把这个与 \(Part 2\) 结合一下,就可以把整个题目解决了。

先想想如何快速判定 \([l,r]\) 能否加入颜色 \(c\)。我们肯定不能把 \([l,r]\) 放进集合再重新跑一遍判定。

因为 \([l,r]\) 肯定要被某个点覆盖,想到仿照 \(Part 1\),尝试搞出来颜色 \(c\) 的每个点可以出现的位置区间

你发现,如果正着跑一遍判定,跑出来的就是每个点最靠右的可行位置 \(vr_x\);反过来,反着跑判定,跑出来的就是每个点最靠左的可行位置 \(vl_x\)

再仔细思考,你发现,对于第 \(k\) 个点,只要它放在 \(vl_k\)\(vr_k\) 之间的一个位置,就存在一种安排其它点的方法,能够覆盖所有区间

证明:设 \(k\) 的位置为 \(p_k\)

考虑把 \(i<k\) 的点 \(i\) 都放到 \(vr_i\),发现左端点 \(\leq p_k\) 的区间均被覆盖。

\(i>k\) 的点 \(i\) 都放到 \(vl_i\),发现右端点 \(\geq p_k\) 的区间均被覆盖。

于是,在这个方案下,所有区间都被覆盖。

并且,根据假设,当 \(p_k \notin [vl_k,vr_k]\) 时不存在合法方案。

因此,只要 \([l,r]\)\([vl,vr]^{(c)}\) 集合中的某一个区间有交,就说明 \([l,r]\) 可以放入颜色 \(c\);否则就不能放入颜色 \(c\)

(还有一个小结论:所有 \(c,k\)\([vl^{(c)}_k, vr^{(c)}_k]\) 两两不交。)

所以有了这些观察后,我们自然地想到,仿照 \(Part 1\),将每个 \([vl_k,vr_k]\) 维护出来,并放到线段树上,就可以实现快速查找啦!

现在问题转化成,怎么高效维护每个颜色的 \([vl_k,vr_k]\)

Part 4

来点更多观察!

现在我们需要做,在某个颜色中加入一个新区间 \([l,r]\) 后更新 \([vl_k,vr_k]\)

观察到:对于一个区间 \([l,r]\),假如它和不小于两个 \(k\)\([vl_k,vr_k]\) 有交,那么不需要对 \([vl,vr]\) 做任何更新

证明:假设 \([l,r]\) 同时和 \([vl_k,vr_k]\)\([vl_{k+1},vr_{k+1}]\) 有交。

假设 \(k\) 放在了 \(p_k \in [vl_k,vr_k]\)。发现把 \(i<k\) 全部放到 \(vr_k\)、把 \(i>k\) 全部放到 \(vl_k\) 是合法的方案。

对于 \(k+1\) 同理。

其它位置同样使用该构造即可。证毕。

我们发现,当且仅当 \([l,r]\) 与恰好一个 \([vl_k,vr_k]\) 有交时,需要对序列进行更新。

但是只是简单的把 \([vl_k,vr_k]\) 变成 \([vl_k,vr_k] \cap [l,r]\) 是不够的,因为 \([vl_k,vr_k]\) 的缩小,可能会导致别的 \([vl,vr]\) 产生变化。

一个具体的例子:

有一个颜色 \(c\)\(cnt(c)=2\),它当前的区间有 \([1,3], [3,4], [4,5]\)

我们有 \([vl_1,vr_1] = [1,3], [vl_2,vr_2] = [4,5]\)

加入区间 \([1,2]\) 后,\([vl_1,vr_1]\) 更新为 \([1,2]\),但这会同时影响 \([vl_2,vr_2]\) 变成 \([4,4]\)

但是我们又不能整体再跑一遍,重新构造出新的 \([vl,vr]\) 序列。

那咋办。

尝试只更新所有变化了的 \([vl,vr]\)

考虑 \(vr_i\) 是如何计算出来的。当 \(vr_{i-1} = x\) 的时候,所有左端点 \(\leq x\) 的区间都被覆盖。

在此基础上,观察到,\(vr_i\) 等于所有满足 \(l > vr_{i-1}\) 的区间的 \(\min r\)。而这个东西是可以利用数据结构,快速计算的

我们发现,从左到右,第一个变化的 \(vr\) 就是 \(vr_k\),因此这只会影响后面的 \(vr\)

\(k\) 开始,向右逐个计算新的 \(vr_i\) 的值,如果新的 \(vr_i\) 的值和原来相等,就可以停止更新。对于 \(vl\) 也是同理的。

修改 \([vl,vr]\) 序列的同时,要在线段树上面作修改。

我也不知道为什么,但是这个东西均摊好像是对的,听说总共只会修改 \(O(m)\) 次。(反正,写完这个就过了)


最后是,finger search 的部分是 \(O(m \log^2 m)\),维护 \([vl,vr]\) 序列以及线段树的部分是 \(O(m \log m)\),总复杂度是 \(O(m \log^2 m)\) 的。

finger search 套上一些归并排序的手法,应该是可以单 log 的。

finger search 的技巧,以及单 log 的方法可以看这个题

posted @ 2026-07-14 14:16  _Bonely_Muffin  阅读(8)  评论(0)    收藏  举报