這套題仍是有點質量的吧 ……算法
題目連接ubuntu
傻叉簽到題,由於異或的性質因此這個序列的循環節長度只有 \(3\) ……數據結構
查看代碼ide
由於序列長度乃至數的種類都不超過 \(2000\),考慮先把序列離散化。函數
題意讓咱們求一個最短的區間知足以下性質,對於每一種數,其在此區間出現次數不小於在原序列中的出現次數減 \(1\)。spa
能夠先前綴和求一下對於每種數,當前位置及以前的出現次數,和至少一共須要刪掉多少個這種數,即在原序列中的出現次數減 \(1\),方便之後的計算。指針
而後雙指針肯定一個這個區間便可,由於支持$ O(n^2)$ 的算法,因此 \(for\) 每一種數暴力 \(Check\)。rest
查看代碼code
結論題,看到 \(n\) 是 \(4\) 的倍數就天然想到將網格拆成若干個 \(4 * 4\) 的網格來作,每一部分網格依然知足題意的性質,而且拼起來也使大網格知足題意性質。遞歸
結果發現 \(2 * 2\) 的網格便可知足性質…… 對於每一組連續的 \(4\) 個數,存在一種構造方法知足上述性質。
不明白就看代碼吧,挺簡單的。
一道傻叉線段樹由於寫錯遞歸的函數名調了半個多小時…… 屬實降智了……
一開始讀錯題,當作是 \(s_i\) 表示 \(i\) 以前知足 \(p_j < p_i\) 的數的個數,那這道題目的套路就很常見,從後向前推,最後一個數就是當前未選的數中的第 \(s_i + 1\) 個數。
而正確的題意可謂從這上面發展而來,\(s_i\) 表示 \(i\) 以前知足 \(p_j < p_i\) 的數值之和。至關於把上述題意中,一個數的貢獻從 \(1\) 改成了其數值而已。這樣,用線段樹維護前綴和,每次在上面二分查應該到哪一個位置,即當前的數,而後選了的數就單點修改成 \(0\) 來刪除對前綴和的貢獻。
至於一些細節,思路清晰的話試一下就出來了。
沒來得及寫,不過真的沒想到只作 \(4\) 題也上分了……
容易發現每一行都是獨立的,對每一列,咱們只須要把每一行能對這個位置作的最大貢獻加起來就行了,因此對每一行單獨處理。
設 \(len\) 爲當前行的序列長度,當 \(w > 2 * len\) 時,顯然區間 \([len + 1,\ w - len]\) 是能夠取到每個數,包括空位置(貢獻爲 \(0\))的,對這一段區間能夠直接加上 \(max(max\_num,\ 0)\),\(max\_num\) 爲序列中最大值。
如今處理區間 \([1,\ len]\) 和 \([w - len + 1,\ w]\),畫圖總結,對於前者中的每個位置 \(j\),能取到的序列中的數爲 \([max(0,\ j - w + len),\ j]\),對於後者,爲 \([j - w + len,\ min(j,\ len + 1)]\),那麼貢獻就是這段區間中的區間最大值。
注意這兩段區間若是存在重疊部分不要重疊區間計兩次貢獻,至於區間最大值,用 \(st\) 表處理便可,至於貢獻的統計能夠隨便用數據結構作。
思路很巧妙,徹底沒思路…… 本沒腦子選手的水平看來也就半斤八兩,到此爲止了……
考慮按位與的操做只會讓二進制中的 1 變少,所以值域不會變大,能夠對每個數,統計其能夠被與出哪些數,並讓此數 \(x\) 對 \(cnt[x]\) 作出 \(1\) 的貢獻。
巧妙之處在於,若是有兩個數均可以經過與運算獲得 \(x\),那讓這兩個數作按位與,就能夠與出一個二進制上只會比 \(x\) 多出 \(1\) 而不會少的數,即 \(x\) 是其二進制的子集。換句話說,若是用它作或運算,那麼至少能作出 \(x\) 所作的貢獻。
這樣,咱們只想要知道有哪些數能夠被序列中的兩個數作與運算獲得。
能夠 \(Dfs\) 爆枚二進制子集來統計其能夠被與出哪些數,這樣每一個數至可能是 \(O(2^{20})\) 的。可是若是一個數已經處理過兩次了,也就是說它及其它二進制的子集已經都能被某兩個數來與出兩次了,那麼已經達到了咱們的目的,就無需再處理了,因此總漸進時間複雜度是 \(O(n)\) 的。
咱們要求的是當前數與後面某兩個數按位或獲得的最大值,就能夠從上面 \(cnt[]\) 大於 \(2\) 的數中找,從高位到低位貪心地讓 \(0\) 變成 \(1\),這裏能夠結合代碼理解。