小向的試煉 Vol. 3 題解
hansonyu123
1 (Fly)
最直接的想法應該是暴搜(x, y)的所有可能再看看z 是不是整數。然而暴搜的最大困 難點是不知道應該暴搜的範圍有多大。Subtask 2跟Subtask 3分別用兩種方法看範圍,而
Subtask 4則用巧妙的整理技巧化簡問題。
1.1 Subtask 1
既然n ≤3,那就任人宰割啦。隨便暴搜,預處理都ok。這樣就有8分了。
1.2 Subtask 2
因為x≤y≤z,所以
1 n = 1
x +1 y + 1
z ≤ 3 x 也就是說n+ 1≤x≤3n。這樣就知道x的搜尋範圍了。
至於y的範圍,同上面的想法可以知道 y ≤ 2nx
x−n ≤6n2 所以暴搜的複雜度就是O(n3),這樣就有28分了。
1.3 Subtask 3
如果在計算y的範圍的時候不偷懶,那麼要搜的(x, y)對數就有
∑3n
x=n+1
2nx
x−n ≤6n2
∑2n
i=1
1 i
I
所以如果對於每個x,y都只搜到2nx/(x−n)為止的話,複雜度就降為O(n2logn),如此 可獲得52分。
1.4 Subtask 4
上面的暴搜已經是很省時間的暴搜了,然而還是過不了 Subtask 4,所以需要再想 個好一點的方法。既然前面暴搜 (x, y) 失敗了,能不能只枚舉 x 呢?也就是說,已知 (x−n)/xn,需要找到(y, z)使得
x−n xn = 1
y + 1 z 通分移項得到
(x−n)yz−xn(y+z) = 0 為了能因式分解,兩式同乘(x−n)後再同加x2n2 得到
((x−n)y−xn)((x−n)z−xn) = (x−n)2yz−xn(x−n)(y+z) +x2n2 =x2n2 所以只要枚舉所有的x2n2 的因數就可以解出對應的(y, z)了。需要注意的是,要考慮一下 是否要枚舉負因數。不過在本題剛好不用,因為若兩者皆為負,那麼因為x−n >0,
(xn−(x−n)y)(xn−(x−n)z)< xn×xn=x2n2
即得到一個矛盾。
為了快速枚舉因數,先用O(3n)的時間找出所有數的最小質因數(這個用線性篩法可 以做到),再對每個x都用O(logx)的時間找出他的質因數分解式,用O(logn)的時間把 他和n的質因數分解式合併乘以二就找到x2n2 的分解式。接著只需要用O(因數個數)的 時間枚舉因數驗證y, z 是否為整數且x≤ y ≤ z 是否被滿足。因數個數的複雜度很難估,
不過不難想像他小於104,所以這樣子的時間複雜度是可接受的。
總時間複雜度O(n(logn+因數個數)),便可解決此題。
實際上複雜度遠比O(n2)還來得小。之所以只出到104是為了避免x2n2超過long long 範圍。
2 Palembang Bridges
首先不難發現不用過河的人對這個題目的影響是固定的,可以分別計算。再來,對於 所有需要過橋的人,他們走橋的距離也是一樣的,所以也同樣可以分開計算。因此,我們
II
可以把題目化約成給定a1, b1, . . . , aN, bN,要怎麼選定橋的位置c1, . . . , cK 使得
∑|ai−cdi|+|bi−cdi| 最小,其中di = 1, . . . , K 代表第i個人選擇要過的橋。
2.1 Subtask 1
對於K = 1的情況,整個東西就是c1 的函數。不難看出這個函數是一個折線圖,僅 在ai 或bi 曲折。所以最小值一定發生在其中一個ai 或bi。暴搜這些可能性即可。複雜度 O(N2),便可獲得8分。
2.2 Subtask 2
相信大家都在高中的數學課證明過(?)這個函數在 c1 是a1, b1, . . . , aN, bN 的中位數時 會是最小的。所以只需要用O(NlogN)(排序)或O(N)(nth_element)就可以解決了。這樣 便可以拿到22分。
至於證明方法大略就是,如果c1不是中位數的話,不妨設他比較左邊。如此一來,在 右邊的人比在左邊的人多,所以ci 往右靠一點點函數值就會更小。
2.3 Subtask 3
同Subtask 1,將兩座橋的任一座固定時,另一座橋在某一個ai, bi 的時候會達到最
小。所以直接暴搜兩座橋的位置就可以得到一個O(N3)的作法。這樣可以拿到9分(加上 Subtask 1是17分)。
2.4 Subtask 4
如果我能做到:給定兩座橋的位置,馬上知道哪些人走1號橋比較好,哪些人走2號 橋比較好,對整題或許會有幫助。所以我們要來思考|ai−c1|+|bi−c1|以及|ai−c2|+|bi−c2| 的大小關係如何確定。
把cj 在ai, bi之間以及之外這兩個情況分開討論,不難發現
|ai−cj|+|bi−cj|= 2max(|mi−cj|,|mi−ai|)
其中 mi 是ai, bi 的中點。因此,橋的位置離中點越近,這個值就越小。也就是說,對於 一組ai, bi,他們選離中點近的橋走不會更差。因此我們可以將所有點的中點排列,並指
III
定(枚舉)讓中點在某個位置 X 的左邊的人都走一號橋,在某個位置的右邊的人都走二 號橋。於是問題便轉化為一座橋的問題。如此一來總複雜度便是O(N2logN)(排序)或者 O(N2)(nth_element)。這樣就可以拿到63分。
2.5 Subtask 5
如果我們枚舉X 的方法是好好枚舉的方法,比如說由小到大枚舉,那麼不難發現問 題轉化為:每次減少(增加)兩個數,快速求出所有數的中位數。處理這個問題的方法非 常多:你可以直接使用排名樹(比如treap),也可以使用一個min-heap儲存較大的那一半,
用一個max-heap儲存較小的那一半(雖然這樣做只支援增加兩個數,但是只要先把X 由
小到大枚舉,再把X由大到小枚舉就可以用增加替代減少)。不管是哪個,總複雜度都是 O(NlogN)。
事實上還有一個三分搜的方法,不過既然都想到對中點排序以及找中位數了,不如使 用上面的作法比較直覺一點。
本題需要將所計算的值在
∑|ai−cdi|+|bi−cdi|=∑
2max(|mi−cdi|,|mi−ai|) 兩種表示法之間靈活轉換,值得大家好好思考。
3 (Key)
3.1 Subtask 1
直接模擬排序即可。複雜度O(M NlogN)(sort)或O(M N)(counting sort)。24分只需要 這樣就可以拿到了。
3.2 Subtask 2
眼尖的人應該會發現,counting sort 相當於數區間內有幾個a,把a先填前面/後面,
剩下的填b。因此只需要開一個線段樹,記錄該區間內有幾個a,那麼排序一個區間就是
區間查詢以及區間修改。利用懶人標就可以達成這些要求。複雜度O(N +MlogN)。這樣 就有36分。
IV
3.3 Subtask 3
如果發現Subtask 2的作法,那麼Subtask 3也就不難了。只要開26棵線段樹,在每次
排序的時候都從a開始數區間內有幾個,再從前面/後面填a。下一個字元只要接著前一個 字元填的地方繼續填就好了。所以這些就只是26棵線段樹的區間查詢和區間修改,同樣 用懶人標就可以達成要求。複雜度O(26(N +MlogN))便可通過這題所有測資。
另一個作法很簡單。我們可以將原本的序列分成一堆區間,每個區間都是遞增或遞減 排列。如此一來,對於每個區間,我們只在意他是遞增還是遞減,以及裡面有幾個a,幾個
b,...,幾個z。一開始可以視為每個字母都獨立成為一個區間。而每次排序時,只需要看這
次影響到的有哪些區間,數一下這些區間內的a到z有幾個,再將這些區間移除,增加新 的區間。因此可以考慮使用map。
如此一來,不難發現每次操作所增加的新區間至多三個(左邊的渣渣,右邊的渣渣以 及自己),而且每個區間只會被計算以及移除一次。因此總時間複雜度為
O((26 +logN)(N +M)),略好於前一種作法。
V