去年做 Day1 的時候睡了一覺才把 T1 做出來,所以花了大約十多個小時 ak 。
今年從七點半做到三點,也是睡了一覺才做出 T3 ,用了七個半小時。
所以有進步!
寫代碼的過程中可以發現我的犯錯方法關於變量個數指數上升,比如在 T1 中寫出 \(\text{j}\to \text{i}\) , T2 中寫出 \(\text{lf[k]}\to \text{k},\text{fr}\to \text{x}\) 等優秀代碼。我真是個大沙雕。
T1
完全找不出操作有什么性質。雖然可以快速定位出哪里撞了上下界,但是並沒有什么用。
於是決定直接維護神奇分段函數 tag 。但是分段函數顯然又和 \(c\) 有關系。
不過可以發現對於固定的 \(c\) ,tag 可以寫成 \(x+t\to [l,r]\) 的形式,即先給原值加上 \(t\) ,然后卡進區間 \([l,r]\) 里。顯然 \(t\) 和 \(c\) 無關,所以可以維護 \(l,r\) 關於 \(c\) 的分段函數 \(L(c),R(c)\) 。
把這兩個分段函數畫在同一個坐標系里,會發現對於一個前綴 \(c\le pre\) 有 \(L(c)=R(c)\) ,而對於 \(c>pre\) 則 \(L\) 是常值函數, \(R\) 是關於 \(c\) 的一次函數。
另外,兩個函數的每一段都要么是常值函數,要么是斜率為 1 的一次函數。
這時候會發現兩個標記雖然可以合並,但是似乎要用可持久化 fhq treap 硬搞,非常陰間。
此時想起不一定要用線段樹,可以大力分塊。於是其中一個標記的形式就會非常好看,合並的時候就是把另一個標記的前面一段刪掉,把自己加上。
整塊合並標記,零散塊暴力重構即可。復雜度 \(O(q\sqrt n)\) 。
update:有簡單的 \(n\log n\) 做法。
考慮單個元素,在不管上下界的情況下容易把它的取值關於時間的函數畫在坐標系上。此時考慮進上下界,就是每當撞到線時把后面整體平移。
如果能找到最后一次撞到線的時刻就很容易得到最終的答案。
容易發現兩次撞線之間 \(\max-\min\) 必然大於 \(c\) ,而這也是充分條件,所以可以找到這樣一個后綴,后綴中就恰好包含倒數第二次和最后一次撞線的時刻。
區間操作可以差分,用線段樹可以很方便地從一個元素推到下一個元素,也很容易支持二分后綴。
T2
如果正解是把所有 \(p\) 求出來,就肯定不會只求哪些最少了。所以開始想一些亂搞的方法。
顯然到達關系滿足傳遞性。
我們只需要求縮點之后沒有出邊的點,所以容易想到給每個點找到一個出邊,只要自己還沒有和指向的點縮在一起就不需要管自己。
所以維護有根內向森林結構,每次挑出一個根,給它找一個出邊。如果指向另外一棵樹就暫時不用管它,否則把這個環縮成一個點接着做。
問題轉化為怎么找出邊。我的做法是用線段樹按顏色維護每個點的所有出邊、以及自己擁有的鑰匙的顏色。合並兩點時無腦線段樹合並+啟發式合並即可。
復雜度應該是 \(O(n\log n)\) ?
T3
網格圖是二分圖,可是這有什么用呢?
把格子黑白染色,那么一條邊的兩邊必然是一個黑一個白。
我們欽定橫向邊只能占用黑色格子,縱向邊只能占用白色格子。那么橫縱互不影響。
那么一個格子被占用兩次就只有可能它四個角都有點。發現倒數第二檔部分分保證沒有這種情況,所以這個做法可能有希望。
如果某個黑色格子上下都有橫向邊,並且左邊也有一條邊,那么就可以把一個橫向邊換成右邊的縱向邊(而連通性不變),這個格子就活了。此時右邊的格子可能又死了,所以遞歸?
畫畫圖發現遞歸好像不會成環,那看起來挺牛逼。但是這時候想起來這個做法有一個致命錯誤:如果左右都沒有邊怎么辦?
那么考慮用善良一點的順序加邊。比如從上到下從左到右加邊?
加這一行之前保證所有能連通的都連通了,並且無環。
如果加的是橫邊:
- 如果是向下標記那就無腦加。
- 否則如果不會撞也就直接加。否則出現了四方格,那么必然已經被縱向邊連通,不需要管。
如果加的是縱邊:
- 如果是向右標記就無腦加。
- 否則如果沒有撞那也直接加。否則出現了四方格,那么上面兩個點肯定已經連通,就把自己丟到下面去。
發現做完了。