1. 程式人生 > >3675: [Apio2014]序列分割

3675: [Apio2014]序列分割

online 分數 family 超過 times 通過 滿足 數據規模 problem

Time Limit: 40 Sec Memory Limit: 128 MB
Submit: 4522 Solved: 1752
[Submit][Status][Discuss]

Description

小H最近迷上了一個分隔序列的遊戲。在這個遊戲裏,小H需要將一個長度為n的非負整數序列分割成k+1個非空的子序列。為了得到k+1個子序列,小H需要重復k次以下的步驟: 1.小H首先選擇一個長度超過1的序列(一開始小H只有一個長度為n的序列——也就是一開始得到的整個序列); 2.選擇一個位置,並通過這個位置將這個序列分割成連續的兩個非空的新序列。 每次進行上述步驟之後,小H將會得到一定的分數。這個分數為兩個新序列中元素和的乘積。小H希望選擇一種最佳的分割方式,使得k輪之後,小H的總得分最大。

Input

輸入第一行包含兩個整數n,k(k+1≤n)。

第二行包含n個非負整數a1,a2,...,an(0≤ai≤10^4),表示一開始小H得到的序列。

Output

輸出第一行包含一個整數,為小H可以得到的最大分數。

Sample Input

7 3
4 1 3 4 0 2 3

Sample Output

108

HINT



【樣例說明】

在樣例中,小H可以通過如下3輪操作得到108分:

1.-開始小H有一個序列(4,1,3,4,0,2,3)。小H選擇在第1個數之後的位置

將序列分成兩部分,並得到4×(1+3+4+0+2+3)=52分。



2.這一輪開始時小H有兩個序列:(4),(1,3,4,0,2,3)。小H選擇在第3個數


3)=36分。

3.這一輪開始時小H有三個序列:(4),(1,3),(4,0,2,3)。小H選擇在第5個

數字之後的位置將第三個序列分成兩部分,並得到(4+0)×(2+3)=

20分。

經過上述三輪操作,小H將會得到四個子序列:(4),(1,3),(4,0),(2,3)並總共得到52+36+20=108分。

【數據規模與評分】

:數據滿足2≤n≤100000,1≤k≤min(n -1,200)。

先膜一發黃學長,想了好久才想明白,f[i]的計算是倒過來的

仔細觀察,可以發現分割順序是不影響最終結果的,所以我們可以從右邊開始分割(想了一個小時。。。)

f[i]=max{f[j]+(s[i]-s[j])*s[j]}

斜率優化(註意變號):

\[\frac{f[j]-f[k]+s[k]^{2}-s[j]^{2}}{s[k]-s[j]}<s[i]\]

需要加個滾動數組節省空間

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 
 5 #define LL long long
 6 const int MAXN=100005;
 7 
 8 int n,k;
 9 int a[MAXN],q[MAXN];
10 LL f[MAXN],g[MAXN],s[MAXN];
11 
12 double slope(int k,int j)
13 {
14     return (double)(g[j]-g[k]+s[k]*s[k]-s[j]*s[j])/(s[k]-s[j]);
15 }
16 
17 void dp()
18 {
19     int l=0,r=0;
20     for(int i=1;i<=n;i++)
21     {
22         while(l<r&&slope(q[l],q[l+1])<s[i]) l++;
23         int t=q[l];
24         f[i]=g[t]+(s[i]-s[t])*s[t];
25         while(l<r&&slope(q[r-1],q[r])>slope(q[r],i)) r--;
26         q[++r]=i;
27     }
28     for(int i=1;i<=n;i++) swap(f[i],g[i]);
29 }
30 
31 int main()
32 {
33     scanf("%d %d",&n,&k);
34     for(int i=1;i<=n;i++)
35     {
36         scanf("%d",&a[i]);
37         s[i]=s[i-1]+a[i];
38         if(a[i]==0) i--,n--;
39     }
40     for(int i=1;i<=k;i++) dp();
41     printf("%lld",g[n]);
42     return 0;
43 }

3675: [Apio2014]序列分割