1. 程式人生 > >【BZOJ3233】找硬幣(AHOI2013)-DP+數論

【BZOJ3233】找硬幣(AHOI2013)-DP+數論

測試地址:找硬幣
做法: 本題需要用到DP+數論。
假設我們有了構造出了一個合法硬幣序列 x x ,怎麼計算最少需要使用的硬幣數量?顯然,因為 x k x_k

x k 1 x_{k-1} 的倍數,能用大的就應該用大的,那麼對於最大的幣值 x
k x_k
,應該要使用 a i x k
\lfloor\frac{a_i}{x_k}\rfloor
個,於是還剩下 a i % x k a_i\%x_k 需要支付,於是對於第二大的幣值 x k 1 x_{k-1} 需要使用 a i % x k x k 1 \lfloor\frac{a_i\%x_k}{x_{k-1}}\rfloor 個,於是還剩下 a i % x k 1 a_i\% x_{k-1} 需要支付…以此類推。於是我們得到答案:
a n s = i = 1 n ( a i x k + j = 1 k 1 a i % x j + 1 x j ) ans=\sum_{i=1}^n(\lfloor\frac{a_i}{x_k}\rfloor+\sum_{j=1}^{k-1}\lfloor\frac{a_i\%x_{j+1}}{x_j}\rfloor)
我們發現 x j x_j 之間互相產生貢獻,會且只會在相鄰的 x j x_j x j + 1 x_{j+1} 之間,以及最後再補上一個 a i x k \lfloor\frac{a_i}{x_k}\rfloor ,也就是說 x j x_j 的選取是一個可以多階段決策的問題,也就可以用動態規劃解決了。
為了方便,我們先把 a i x k \lfloor\frac{a_i}{x_k}\rfloor 那個部分略掉(因為可以 O ( n max a i ) O(n\cdot \max a_i) 算出),令 f ( p ) f(p) x k = p x_k=p a n s ans 的最小值,那麼有狀態轉移方程:
f ( x ) = min { f ( y ) + i = 1 n a i % x y } ( y x ) f(x)=\min\{f(y)+\sum_{i=1}^n\lfloor\frac{a_i\%x}{y}\rfloor\}(y|x)
邊界為 f ( 1 ) = 0 f(1)=0 。這個東西每次轉移都暴力計算是 O ( n ) O(n) 的,而鑑於從 f ( p ) f(p) 可以轉移到 p p 的所有的倍數,因此轉移次數是一個調和級數,也就是 O ( max a i log ( max a i ) ) O(\max a_i\cdot \log(\max a_i)) 次轉移,那麼總的時間複雜度為 O ( n max a i log ( max a i ) ) O(n\cdot \max a_i\cdot \log(\max a_i)) ,爆炸的可能性很大(我沒試過,但應該會掛)。
於是我們需要找到 O ( 1 ) O(1) 轉移的方法,唯一的方式只有預處理出一部分答案,而上面那個 i = 1 n a i % x y \sum_{i=1}^n\lfloor\frac{a_i\%x}{y}\rfloor 實在有點糾結,我們考慮怎麼把 a % x y \lfloor\frac{a\%x}{y}\rfloor