題解:[COCI2011-2012#5] BLOKOVI
題解:[COCI2011-2012#5] BLOKOVI
Description
PDF : https://hsin.hr/coci/archive/2011_2012/contest5_tasks.pdf
題意:
有 \(N\) 個已知質量的矩形,長都是 2 ,高都是 \(h\) 。把它們放到一個平面直角座標系裡,滿足:
- 各矩形的邊與座標軸平行;
- 各矩形下底邊的縱座標各不相同,分別為 \(0,h,2h,\dots,(N-1)h\) ;
- 最底端的矩形左下角座標恆為 \((-2,0)\) ,右下角為原點 \((0,0)\) 。
我們記一個矩形的 \(Xcentre\) 為其下底邊的中點,
一個或多個矩形的 \(Xbarycentre\)
其中 \(m_i\) 是各個矩形的質量。
如果 \(N\) 的矩形的擺放方案是穩定的,它應當滿足對於任意的矩形 \(A\) ,在 \(A\) 上方的各矩形的 \(X-barycentre\) 符合 \(|Xbarycentre - Xcentre(A)| \leq 1\) 。
左圖的擺放方案就是不穩定的,因為最上方兩個矩形的 \(Xcentre\) 距離落在的下面那個矩形的右側。
右圖的的拜訪方案則是穩定的。
現在我們給出從下至上各個矩形的質量,在保證擺放方案穩定的情況下,請你找出所有矩形右下角頂點的橫座標最大值可能是多少。
注意,你不能改變矩形的擺放順序和第一個矩形的位置。
\(2\leq N \leq 3e5\)
至今,我仍然無法用自然流暢的語言翻譯英文題面……大家姑且一看,建議還是讀原題。
Algorithm
太神了,我只能說不愧是克羅埃西亞題。
首先, \(N\leq3e5\) 的規模要求了一個線性的演算法,這似乎是比較困難的。
為什麼呢?我們自底而上地考慮,考慮放置某個矩形的時候,未來放置的其它矩形很可能會影響它的放置。
換言之,我們需要「回頭」。
說到「回頭」,我們很容易想到動態規劃中「後效性」的概念。
而這道題正是一個 多階段決策最優化問題 。我們不妨就試著消除後效性,使用動態規劃來解決它。
試著消除本題的後效性是一件並不困難的問題。只需要回顧剛剛我們需要「回頭」的情形,很容易就能發現:
任何一個矩形的放置方法只可能影響在它之下的矩形放置。
當然上面矩形的絕對座標也會受到影響,但是相對位置並不會發生變化。
因此,我們當然可以自頂向下地考慮各個矩形的放置,這樣就自然可以消除後效性了。
當然,題目還要求最底下的那個矩形的位置固定,因此我們需要將自頂向下考慮得到的相對座標轉化為答案要求的絕對座標。
既然後效性沒了,那麼接下來,我們考慮動態規劃的狀態轉移。
我們在這裡仍然存在一個小小的問題:
我們是在一個笛卡爾座標系上放矩形,可以放置的位置可是有不可數無窮多個啊。
直覺敏感的同學或者勇敢莽撞的同學一定發現了:我們只要往最左邊或者最右邊放就行了。
這點是容易感性地證明的:
考慮不是把某個矩形放在最左或最右的情況,將其與其下的矩形在不改變其相對位置的情況下、整體地左移或右移,一定可以使得答案不變得更劣。
這句話稍稍有點複雜,不過其意涵是比較容易理解的。
每個矩形能放置的極限位置也是容易計算的。我們先計算向右放最遠能放多少。
記
\[\begin{align*} &d_k = Xcentre(k) - Xbarycentre(k + 1\sim n) \leq 1 \end{align*} \]由定義又有:
\[Xbarycentre(k\sim n) =\frac{ (\sum_{i = k + 1}^n m _i) \cdot Xbarycentre(k+1\sim n) + m_k \cdot Xcenter(k)}{\sum_{i = k}^n m_i} \]其中,
\[Xcentre(k) = d_k + Xbarycentre(k + 1 \sim n) \]然後一個帶入,
\[Xbarycentre(k\sim n) = Xbarycentre(k+1 \sim n) + d_k \cdot \frac{m_k}{\sum_{i=k}^n m_i} \]按照上述分析,直接取 \(d_k = 1\) ,那麼放置導致的重心向右偏移量即為 \(\Large \frac{m_k}{\sum_{i = k}^n m_i}\) 了。
由對稱性,向左放最大偏移量即為 \(2 - \Large \frac{m_k}{\sum_{i = k}^n m_i}\) 。
接下來我們只要順次考慮某個矩形是放在最左還是最右即可了,這是一個類似 01揹包的模型。
程式碼很簡單:
#include<bits/stdc++.h>
using namespace std;
template<class T>
inline void read(T &x)
{
char c = getchar(); x = 0;
while(c < '0' || '9' < c) c = getchar();
while('0' <= c && c <= '9')
{
x = (x << 1) + (x << 3) + c - 48;
c = getchar();
}
}
const int N = 3e5 + 10;
int n, a[N];
int main()
{
read(n);
for(int i = 1; i <= n; read(a[i++]));
double ans = 0, sum = 0;
for(int i = n; i > 1; --i)
{
sum += a[i];
double del = a[i] / sum;
ans = max(ans, max(ans + del, 2 - del));
}
printf("%.8f\n", ans);
return 0;
}