Problem D
其實是道很簡單的二分題但考場上我一直想的是貪心。
每一次操作,任選一個不變其餘減 \(1\)。
那麼二分答案,對於二分到的答案 \(mid\),我們計算 \(\sum\limits_{a_i<mid}(mid-a_i)\),如果小於等於 \(mid\) 就是合法答案。
時間複雜度 \(O(n\log n)\)。
Problem H
很容易想到的一個逆向思維是,所有的人不動,而是電線杆動。
那麼怎麼解決電線杆與分身的碰撞呢?我們以橫向運動為例。
假設電線杆現在在 \((x_0,y_0)\) 點,要向右移動距離 \(l\),那麼根據題意,滿足 \(x\in[x_0,x_0+l]\) 且 \(y=y_0\) 的分身都會被合併到點 \((x_0+l+1,y_0)\) 上。
合併直接用並查集即可,關鍵在於如何找到這些該合併的點。
我們考慮以 \(x\) 升序最為第一關鍵字,\(y\) 升序作為第二關鍵字排序,不難發現的是,我們需要的點在一個區間上。
由於需要動態維護,我們用 set 維護,操作的時候 lower_bound 找到所需區間第一個點即可。
同理,處理上下移動時,我們需要以 \(y\) 升序最為第一關鍵字,\(x\) 升序作為第二關鍵字排序,需要再來一個 set。
對於時間複雜度,類似於啟發式合併,是 \(O(n\log n)\)。
Problem I
分情況討論:
- 不在最短路上,且邊變大:一定不會是新的最短路。
- 不在最短路上,且邊變小:將經過這條邊的最短路與原來的最短路比較即可。
- 在最短路上,且邊變小:原來的最短路減去對應差值即可。
- 在最短路上,且邊變大:新的最短路可能是原來的最短路加上這段差值,也可能是刪掉這條邊做最短路。
因此,這道題中最複雜的部分其實就是如何做刪邊最短路。
我們先隨便取出一條最短路,形成一條鏈。類似於斷環法的思路,我們考慮刪掉這條鏈上的一條邊之後,以一條路徑繞過這條邊。
比如一條路徑從 \(x\) 點從這條鏈出去,從 \(y\) 回來,那麼這條路徑就能作為 \(x\) 到 \(y\) 鏈上所有斷邊的"替代"。
按照這個思路,我們列舉每一條邊 \((u,v,w)\),嘗試用 \(d_1(u)+d_2(v)+w\) 來更新對應的區間。由於到 \(u\) 是用的最短路(即 \(d_1(u)\)),因此我們在找 \(x\) 點的時候也要滿足最短路的要求(即 \(1-x-u\) 的最短路就是 \(1-u\) 的最短路),再跑一遍最短路就可以完成找 \(x\) 點。找 \(y\) 點的時候同理。
由於只需要區間取最小值,我們用線段樹維護即可。
時間複雜度 \(O(n\log n)\)。
Problem M
這好像是我做過的原題,但是時間太久遠了我給忘了。
考慮到 \(n\) 很小,我們可以列舉行的所有狀態,再列舉列貪心選擇(\(1\) 比 \(0\) 多就翻),時間複雜度是 \(O(2^nm)\),需要優化。
設 \(a_i\) 表示狀態為 \(i\) 的列的個數,\(b_i\) 表示在列在狀態 \(i\) 時貪心選最少的 \(1\) 的個數。
舉個例子,對於狀態 \(10111100\),\(b_i=3\);對於狀態 \(00010100\),\(b_i=2\)。
那麼我們列舉行的狀態 \(T\),有:
由於 \(T\bigoplus i=j\) 與 \(i\bigoplus j=T\) 等價,轉化成:
後半部分是 FWT 的模板,直接做即可。
要注意的是,雖然答案在 \(nm\) 以內,但是做的時候可能會超 int 範圍,要開 long long。
時間複雜度 \(O(n\log n)\)。