1. 程式人生 > >LOJ.2585.[APIO2018]新家(二分 線段樹 堆)

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\)比較。