LOJ.2585.[APIO2018]新家(二分 线段树 堆)

LOJ
BZOJ
洛谷

把一个Delete写成Insert
还有st[col]写成st[p]
别的就和我四个小时前写的差不多了?==
我这调的四个小时究竟在干什么==

首先考虑离线,将询问按时间排序。对于每个在\([l,r]\)出现的颜色,拆成在\(l\)加入和\(r+1\)删除两个操作,也按时间排序。

对于询问\((x,t)\),就是求\(t\)时刻,离\(x\)最远的颜色到\(x\)的距离,也就是从\(x\)出发往左右至少要走多远才能经过所有颜色。
考虑二分答案。那么就成了,求所有颜色是否都在\([x-mid,x+mid]\)中出现过。

对于这种是否出现过/只计算一次的问题,通常是对每种颜色计算从左到右第一个出现的颜色。
对每个位置\(i\)\(pre_i\),表示\(col_i\)上次出现的位置。那么\(i\)\(col_i\)颜色中,该区间第一个出现的当且仅当\(pre_i<l\)
所以我们对区间求\(pre_i<l\)的位置个数就是答案了。但这好像要树套树。。于是复杂度就成了\(O(n\log^3n)\)。。

显然有点想偏。再看我们要求的问题:区间中是否出现过所有颜色。我们不需要求有多少种颜色出现了,只要能找到一种不在区间中出现过的颜色就可以了。
如果一种颜色不在\([l,r]\)中出现过,那么它的\(pre_i<l\)\(i>r\)。也就是说我们求\([r+1,n]\)中是否存在\(pre_i<l\)就可以了,即求\(pre_i\)的最小值。
每种颜色的\(pre_i\)可以开\(k\)\(set\)维护。
因为同一个位置可以有多种颜色,每个位置的\(pre_i\)会有很多且可能相同。所以对于每个位置还要用一个\(multiset\)或堆来维护\(\min\{pre_i\}\)并支持删除。

这样就OK啦,复杂度\(O(n\log^2n)\)

再考虑一下二分能否直接在线段树上二分。实际上是可以的。
orz kcz
二分一个\(mid\),如果\(Ans\geq mid\),则\((x-mid,x+mid)\)中不含所有颜色,即\([x+mid,n]\)中最小的前驱\(mn\)满足\(mn\leq x-mid\)
我们实际是要求一个最大的\(i\),使得\([i,n]\)中最小的前驱\(mn\),仍满足\(mn+i\leq 2x\)\(i\)越大则\(mn\)越大,越容易不满足条件)。此时答案就是\(\min\{i-x,\ x-\min\{pre_i\}\}\)(一个是右端点一个是左端点)。
怎么在线段树上求最大的\(i\)呢。
先判一下无解情况。
假设现在是在线段树的\([l,r]\)区间:
\(x\)落在\([mid+1,r]\)区间,则\(i\)也一定落在\([mid+1,r]\)区间。
\(x\)落在\([l,mid]\)区间,则要判断一下\(i\)能否落在\([mid+1,r]\)区间。因为\(i\)越大\(mn\)越大,所以只需要判下\(i=mid+1\)时是否可行就行了。

这样就一个\(\log\)啦。

注意求的\(\min\)\([i,n]\)的,如果递归到\([l,mid]\)要与右区间取\(\min\)
另外线段树上的节点以及\(mn\)是离散化后的值域,比较的时候用\(ref[mid]\)(实际值)与\(x\)比较。

猜你喜欢

转载自www.cnblogs.com/SovietPower/p/10068207.html