NOI2016 國王飲水記

2022-08-15 03:00:20 字數 1390 閱讀 6977

首先,我們一定可以捨去那些高度比 \(h_1\) 還小的城市,並且將剩餘的高度比 \(h_1\) 大的城市排序,使得 \(h_1\) 到 \(h_n\) 遞增。

我們不妨從三座城市想起。假如可以合併兩次,應該怎麼合併?

先合併 \((1,2)\),再合併 \((2,3)\),因為這樣更多的水被貢獻給了 \(1\),對吧?

由此我們得出了第乙個obeservation:越早合併的城市,高度越低。這也就意味著,我們每次合併的城市,必定是高度遞增的一段,且前一次合併的所有城市的水量都低於後一次合併的城市。

那如果只能合併一次呢?

首先,\((1,3)\) 肯定是要合併的。那麼,\(2\) 要不要並進去呢?這就要看 \((1,3)\) 合併後,\(2\) 是否比 \(1,3\) 的高度高了。

於是我們得到了用一次合併來合併城市 \(1\) 與某段城市 \([l,r]\) 的策略:先合併城市 \(1\) 和 \(r\),然後依次合併 \(r-1,r-2,\dots,l\),直到並進去會使得平均值減小。

有了這兩個觀察,我們就可以開始dp了:設 \(f_\) 表示前 \(i\) 個位置合併 \(j\) 次的最優答案。則 \(f_=\max\limits_\) 還要與 \(f_,f_,\dots,f_\) 取 \(\max\),用來表示位置 \(i\) 不合併到某段區間中。

這種「合併 \(k\) 次」的dp,要優化肯定要按照合併次數dp。於是我們設 \(f_i\) 表示當前的dp陣列,\(g_i\) 表示上一輪dp(合併次數少一)的dp陣列。

則式子修改為 \(f_i=\max\limits_

int main()

printf("%lf\n",f[n]);

return 0;

}然後,有乙個推論是「合併 \([l,r]\) 時實際上是合併了 \([l,r]\) 中一段最優的字尾 \([l',r]\)」這個東西實際上是沒有必要的,因為若 \([l,r]\) 只合併了 \([l',r]\),而 \([l,l'-1]\) 未被合併,但是以 \(l-1\) 結尾的一段區間卻被合併了的話,我們完全可以將它向右移至以 \(l'-1\) 結尾,並使得答案更大。因此,這個結論變換為,所有被合併過的位置是 \(h\) 陣列的一段字尾。(雖然這個結論是我在寫題解時才發現的,因此**中完全沒有用到這個結論)

這之後,我們掏出轉移式 \(f_i=\max\limits_

void solve()

void solve(int id)

for(int i=1;i<=n;i++)if(f[i]}decimal calc(int i,int j)

int main()

decimal res=calc(lim,n-m+lim);

for(int i=n-m+lim+1;i<=n;i++)res=(res+h[i])/2;

cout

}

國王飲水記 題解

題目傳送門 step1 暴力 對於每次詢問,都進行一次krusal計算。include include define maxn 5039 using namespace std inline intread if flag return sum return sum struct jtz e max...

NOI2016 優秀的拆分

看到題目,資料範圍有點怪異。對於95 的資料,對於100 的資料,意思是只有5分是正解。好吧,95pts的 很明顯,答案就是 而如何才能拿到100pts呢?我們可以先列舉a段的長度,很明顯每個長度為lcp,與往後求lcs,若 這樣就可以通過 include include include inclu...

NOI2016 優秀的拆分

題目實際上要求我們求從每個點出發的aa串的數量 考慮點i的答案,發現如果字首i與字首j j i 的最長公共字尾 i j,那麼i點出發向前就存在乙個長度為i j的aa串,題目即求對於每個字首,有多少個在他之前的字首滿足條件 考慮字尾自動機,由於每個字首都是字尾自動機parent樹上的一點,即兩個字首的...