[Codeforces] 1514D

Cut and Stick

窩耍毒瘤 QQ

題敘

有一個陣列, 多筆詢問一個區間 $[l, r]$, 問這個區間可以最少被分成多少個可以不連續的子序列, 使得每個分別的子序列中最常出現的數都不超過 $\Big\lceil \frac{\ell}{2} \Big\rceil$,其中 $\ell$ 是子序列的長度。

提示

Hint 1

一個區間的答案是 $1$ 或其他數跟什麼性質有關?
  

Hint 2

假如只詢問一個區間,眾數的數量跟答案有什麼關係?
  

Hint 3

現在知道全域的做法,要怎麼有效的維護絕對眾數(出現數量過半的數字)?
  

Hint 4

有什麼資料結構/算法可以解決這個問題?
  

想法

觀察

可以發現一個區間的答案跟是否有絕對眾數有關。 而且,區間數字的順序不影響答案。

設區間長度為 $\ell$,區間眾數出現 $c$ 次, 則有一個方法永遠是最佳解: 把每個絕對眾數跟其他數兩個兩個一組, 這樣每一組不論放在哪個子序列都不會影響解的正確性。

所以答案就是剩下未分組的元素, 就是 $\ell - (\ell - c) \times 2 = c \times 2 - \ell$, 沒有絕對眾數的答案就是 $1$,所以答案就是 $\max(1, c \times 2 - \ell)$。   

套用資料結構

有莫隊優化的作法,複雜度 $O(N \sqrt{N})$,已經能 AC 了。
  
不過這樣並不夠好(雖然能 AC), 如果一個陣列中某個是是絕對眾數,他左右半至少有一個子陣列絕對眾數也一樣, 所以可以考慮線段樹維護這個性質, 把查詢區間的 $\log N$ 個節點範圍內的區間絕對眾數都求一次取max, 而每個數的出現次數可以套vector用二分搜(當然也可以用動態開點線段樹,像我就這樣毒瘤), 這樣複雜度是 $O(N \log N + Q \log^2{N})$,也夠 AC。
  
當然如果不想這樣麻煩也可以區間內隨機取 $p$ 個數, 沒有選到絕對眾數的機率是 $2^{-p}$, 只要取 $40$ 個上下就夠, 理論複雜度是 $O(40 Q \log{N})$, 不過常數頗大,有沒有方法再更快?   
首先有一個線性判斷絕對眾數的方法,稱為摩爾投票法(俗稱戰鬥線段樹),
過程是相同 counter+​+,不同 counter-​​​​​​-cnt = 0 就直接換數字, 這樣做並不保證是絕對眾數,但我們只要用前面的方法做一次就有答案了, 由於絕對眾數問題是無序的,所以可以透過線段樹作區間合併, 合併的規則就是相同 counter 相加,不同就比大小再決定怎麼相減, 這樣區間查詢就縮到 $O(\log N)$, 可以有很好的複雜度 $O(N \log{N} + Q \log N)$ 且常數不大。   

AC Code

注意我的區間查數量毒瘤了 QAQ,所以建議不要參考,這題實作難度不高。
Link