[Solution]

這是本場比賽唯一的演算法題(?

AC 做法

這題要 AC 十分簡單,就把每個數字轉成 $\lceil\log_2 N\rceil$ 長度的二進位表示,全部接在一起就可以了,複雜度 $O(N\log N)$。這樣如果 $D=E=1$ 可以拿到 75.154 分。

高分的做法

因為前面的數字確定之後,後面的數字其實就沒有那麼多種可能性了。考慮對於排列中的每個數字,在它前面有幾個數字比它小就把它減幾,這樣第 $i$ 個數字最大就只會到 $N-i$,用 $\lceil\log_2 (N-i)\rceil$ 個 bit 就可以編碼了。解碼就會變成拿到一個數字 $k$,找目前還沒有用過的數字中第 $k$ 大的數字是多少。這些操作 BIT 都可以支援(解碼要在 BIT 上二分搜),複雜度還是 $O(N\log N)$。這樣基本上還是可以維持 $E=D=1$,可以拿到 147.184 分。

再更高分一點的做法

在做完上述的轉換後,可以考慮把幾個數字組合成一個數字編碼(對於上限是 $L_i$ 的 $i$ 個數字 $a_i$ 就組合成 $\sum \left(a_i\prod_{j=0}^ {i-1} L_j\right)$),這樣可以再省下一點點 bit。把所有數字全部組合在一起的話就可以達到理論上最短的長度,然而這就需要做大數乘除法,除了本身就很難寫以外,複雜度會變成 $O(N^ 2)$(除非把階乘分組、硬寫 FFT + 牛頓法除法的話可以勉強壓到 $O(N\log^ 2 N)$ 之類的),而且解碼常數很大(要 mod)只能用在小測資,所以寫起來沒有很值得。
比較好做的做法可以考慮每兩三個數字組合在一起控制在不要超過 long long 的範圍。如果能找一個 1 到 $N$ 的分組方法,使得同一組的數字乘起來都很接近但不到 2 的整數次方,這樣也可以很接近最佳解。
一個簡單的方法是 greedy 兩兩併組。考慮對 2 到 $N$ 的每個非 2 的次方的數字離他的上一個 2 的次方的相對距離排序(實作上可以把每個數字的 highbit 挪到同一個位置,例如 x << (__builtin_clz(x) - 2)),那麼對於某兩個併起來可以省下一個 bit 的數字 x, y,所有這個順序之下在 y 前面的數字跟 x 併起來也都可以省下一個 bit。因此我們可以對每個數字找還沒用過、順序盡量後面且跟它組合起來可以省 1 個 bit 的數字併起來,如果對某個數字找不到就不要併。這個做法可以比前一個做法省下將近 $N/2$ 個 bit,實作上大概可以拿到 165-169 分左右,端看實作常數大小而定。
另外一個做法是既然 $N$ 值都已經給定了,可以先在本機用各種方法想辦法搜出一個好的分組方式放在 code 裡。

順帶一提,如果不考慮 hack 的話,這題的理論最高分是 $\frac{156.0964\times 10+ 170.2301\times 14+ 171.7892\times 22+ 172.0720\times 15+ 172.1213\times 16+ 172.1295\times 23}{100} = 170.175$ 分。

小 hack (?

對於第一個 subtask,因為測資筆數不多,且 $L$ 每減少 1 就可以增加比較多的分數(0.5 分),可以傳一些 submission 把幾個數字 assert 出來然後 hardcode 進去。雖然要求每一個字串長度都相同,但因為可以把字串平均分配在每個字串裡面,所以每讀出價值 10 個 bit 的資料 $L$ 就可以減少 1。

新功能

這題用到的新功能也蠻明顯的:
(1) 可以有這種需要一筆測資執行多次的題目了!
(2) Special judge 可以根據前面幾次的執行結果決定後面的時限。
(3) 可以設定整題分數的精度(例如雖然 special judge 每筆測資輸出的結果是四位小數,但最終分數是四捨五入到三位小數)。