傳送門 之前我也遇到過一次這種「兩段之和乘積作為貢獻「的問題:考慮把這一種\((\sum) *(\sum)\)的形式拆括號,就可以發現貢獻其實就是分別處於左右的兩兩元素乘積之和。
題目的分割\(k\)次,其實就是要你把序列分成\(k+1\)段。
再考慮在題目中如此的分割方法下,貢獻是怎麼產生的。對於每乙個元素\(a_i\),每次分割時,若涉及到自己,則會貢獻一定的乘積\(a_i(\sum)\)。細心想一想就會發現,這個\(\sum\)的總值就是在最終方案下不處於\(a_i\)所在段的元素之和。
單向考慮每一項乘積,(這裡有點跳)總的來算,每一段\([l,r]\)的貢獻就是\((\sum_^ra_i)(\sum_^a_i)\)。記\(a\)的字首和為\(s\),則貢獻是\((s_r-s_)s_\).
我們可以寫出dp式,\(f_\)表示前\(i\)個數恰好分成\(j\)段的貢獻最大值:
\[f_=\max\+(s_i-s_k)s_k\}\;\;(k
直接dp是\(\mathcal o((k+1)n^2)\)的。而看到這個式子比較簡單,考慮斜率優化。這裡省去第二維,整體做\(k+1\)次即可。
設\(k,\(j\)比\(k\)優,則有:
\[\begin
f_j+(s_i-s_j)s_j&>f_+(s_i-s_k)s_k\\
f_j+s_is_j-s_j^2&>f_k+s_is_k-s_k^2\\
s_i(s_j-s_k)&>(s_j^2-f_j)-(s_k^2-f_k)\\
\frac&
直接做就可以了。
#include #include using namespace std;
typedef long long ll;
const int n=100005;
const ll inf=1ll<<62;
const double eps=1e-6;
int n,m,a[n];
int q[n],head,tail;
ll s[n],real_f[n],real_g[n],*f=real_f,*g=real_g;
inline double slope(int a,int b)
int main()
n=cnt; m=min(m,n-1)+1;
for(int j=2;j<=m;j++)
swap(f,g);
} printf("%lld\n",g[n]);
return 0;
}
bzoj3675 APIO2014 序列分割
time limit 40 sec memory limit 128 mb submit 1468 solved 607 submit status discuss 小h最近迷上了乙個分隔序列的遊戲。在這個遊戲裡,小h需要將乙個長度為n的非負整數序列分割成k 1個非空的子串行。為了得到k 1個子序列...
BZOJ3675 Apio2014 序列分割
portal 根據乘法分配律,其實最後答案就是分割後,對每個塊的和,兩兩求乘積加和。那麼割的順序就沒有影響了。遞推式可以寫成 f i k mi n f j k 1 sum i sum j sum j 那麼對於k j且決策k優於決策j f k l 1 sum i sum k sum k f j l 1...
BZOJ 3675 Apio2014 序列分割
bzoj 3675 apio2014 序列分割 小h最近迷上了乙個分隔序列的遊戲。在這個遊戲裡,小h需要將乙個長度為n的非負整數序列分割成k 1個非空的子串行。為了得到k 1個子序列,小h需要重複k次以下的步驟 1.小h首先選擇乙個長度超過1的序列 一開始小h只有乙個長度為n的序列 也就是一開始得到...