APIO2014 序列分割

2022-05-08 06:18:10 字數 2688 閱讀 7721

將乙個長度為\(n\)的序列分成\(k\)段,每次分割一段長度\(\ge 2\)的序列,

得分為兩邊序列元素和的乘積,求最大得分

\[2\leq n\leq100000,1\leq k\leq\min\,0≤a[i]≤10^4

\]我們發現一對元素\((i,j)\)產生貢獻\(a[i]a[j]\)的條件是分割後元素不在同一段裡

於是我們知道一對元素產生是否產生貢獻分割順序無關

那麼這道題就變成了序列分段問題

設\(f[i][k]\)表示\([1,i]\)的序列被分為\(k\)段的最大貢獻,\(s[i]=\sum_^a[j]\),有

\[f[i][k]=max_^(f[j][k-1]-s[j]^2+s[i]s[j])

\]此時可以斜率優化維護上凸包

插點\((s[i],f[i][k-1]-s[i]^2)\),詢問\(k_i=-s[i]\),

由於斜率單調遞減,同樣是可做的

然而這裡有另外一種思路

如果把長度為\(n\)的序列分為\(n-1\)段,

由上可知得分為\(\sum_^\sum_^[i!=j]a[i]a[j]\)

而當一對元素\((i,j)\)不產生貢獻\(a[i]a[j]\)時,他們在同一段裡

於是我們可以求反貢獻的最小值,即對於每一連續段\([i,j]\),

其反貢獻為\(\sum_^\sum_^[k!=l]a[k]a[l]\)

那麼這題可能可以用區間dp做?我還是老老實實寫斜率dp吧

設\(f[i][k]\)表示\([1,i]\)的序列被分為\(k\)段的最小反貢獻,\(t[i]=\sum_^a[j]^2\),有

\[f[i][k]=min_^[f[j][k-1]+\frac((s[i]-s[j])^2-(t[i]-t[j]))]

\]\[=min_^(f[j][k-1]+\frac(s[i]^2+s[j]^2-2s[i]s[j]-t[i]+t[j]))

\]\[=min_^(f[j][k-1]+\frac(s[j]^2+t[j])-s[i]s[j])+\frac(s[i]^2-t[i])

\]那麼我們用普通的斜率優化插點\((s[j],f[j][k-1]+\frac(s[j]^2+t[j]))\)

(注意插點順序),詢問\(k_i=s[i]\)即可,

最後答案為\(\frac(s[n]^2-t[n])-f[n][k+1]\)

小細節:

(1)

該題可以先列舉\(k\)然後列舉\(i\)一層一層做,

或者維護\(k+1\)個凸包,然後倒著插點(!!!)

(2)

該題既可以通過維護上凸包求最大值,

也可以通過維護下凸包求最小值

(3)

注意題中\(a[i]\ge0\),因此\(dx\)有可能為0,直接算斜率的童鞋們要注意啦

#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#define mp make_pair

#define pub push_back

#define puf push_front

#define pob pop_back

#define pof pop_front

#define rg register

#define il inline

using namespace std;

typedef unsigned long long ull;

typedef vectorvi;

typedef long long ll;

typedef double dd;

const dd eps=1e-10;

const int mod=1e8;

const int n=100010;

const int k=202;

il ll read()

il void file()

struct node;dequeq[k];

ll n,k,a[n],s[n],t[n],f[n][k];

il void insert(ll t,node q)

il ll query(ll t,ll k)

vi sol;

il void print(int i,int j)

}int main()

for(rg int i=0;i<=k;++i)insert(i,(node));

for(rg int i=1;i<=n;i++));

} }printf("%lld\n",(s[n]*s[n]-t[n])/2-f[n][k+1]);

print(n,k+1);

for(rg int i=sol.size()-1;~i;--i)

printf("%d ",sol[i]);

return 0;

}

最後鳴謝大佬yyb

APIO 2014 序列分割

題目鏈結 演算法 首先 我們發現將一段序列切成若干段所獲得的收益與順序無關 於是我們可以用fi,j表示切i次 前j個數的最大收益 令sumi表示ai的字首和 顯然 fi,j max 斜率優化即可 此題記憶體限制較緊 可以使用滾動陣列優化空間複雜度 時間複雜度 o nk includeusing na...

APIO2014 序列分割

嘟嘟嘟 複習一下斜率優化,感覺已經忘得差不多了 這題切入點在與答案跟切的順序無關。證明就是假如有三段權值分別為 x,y,z 那麼這兩刀不管按什麼順序切,得到的結果都是 xy xz yz 然後就可以dp。令 dp i j 表示前 i 個數切 j 刀的最大得分,於是就有 dp i j max 觀察這個式...

APIO2014 序列分割

題目鏈結 你正在玩乙個關於長度為 n 的非負整數序列的遊戲。這個遊戲中你需要把序列分成 k 1 個非空的塊。為了得到 k 1塊,你需要重複下面的操作 k 次 選擇乙個有超過乙個元素的塊 初始時你只有一塊,即整個序列 選擇兩個相鄰元素把這個塊從中間分開,得到兩個非空的塊。每次操作後你將獲得那兩個新產生...