3675: [Apio2014]序列分割
阿新 • • 發佈:2018-09-09
online 分數 family 超過 times 通過 滿足 數據規模 problem Time Limit: 40 Sec Memory Limit: 128 MB
Submit: 4522 Solved: 1752
[Submit][Status][Discuss]
4 1 3 4 0 2 3
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)。
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 34 1 3 4 0 2 3
Sample Output
108HINT
【樣例說明】
在樣例中,小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]序列分割