標籤:href ret stat pre status sum i++ esc 子序列
3675: [Apio2014]序列分割Time Limit: 40 Sec Memory Limit: 128 MB
[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 Input7 3
4 1 3 4 0 2 3
Sample Output108
首先可以證明分數與切割順序無關dp[i][j]表示到第i個數,分了j組的最大分數可以通過枚舉前j-1組分到了哪兒來轉移狀態轉移方程 dp[i][j]=dp[k][j-1]+sum[k]*(sum[i]-sum[k]) sum表示首碼和第二維壓去,斜率最佳化 一定注意單調隊列的初始值因為分j組至少要有j個數,所以枚舉要從j開始而它必須由前j-1個數分為了j-1組轉移過來所以單調隊列開始有j-1 如果用斜率最佳化分母可能為0,先把0去掉
#include<cstdio>#include<iostream>#define N 100001using namespace std;long long sum[N],now[N],last[N];int head,tail,q[N],a[N];void read(int &x){ x=0; char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) { x=x*10+c-‘0‘; c=getchar(); }}long long up(int k,int j){ return last[j]-last[k]-sum[j]*sum[j]+sum[k]*sum[k];}long long down(int k,int j){ return sum[k]-sum[j];}int main(){ int n,k; read(n); read(k); k++; for(int i=1;i<=n;i++) read(a[i]); int tot=0; for(int i=1;i<=n;i++) if(a[i]) sum[++tot]=a[i]; for(int i=1;i<=tot;i++) sum[i]+=sum[i-1]; int j; long long ans=0; for(int K=2;K<=k;K++) { head=1; tail=1; q[1]=K-1; for(int i=K;i<=tot;i++) { while(head<tail && up(q[head],q[head+1])>=down(q[head],q[head+1])*sum[i]) head++; j=q[head]; now[i]=last[j]+sum[j]*(sum[i]-sum[j]); while(head<tail && up(q[tail-1],q[tail])*down(q[tail],i)>=up(q[tail],i)*down(q[tail-1],q[tail])) tail--; q[++tail]=i; } ans=max(ans,now[tot]); swap(now,last); } printf("%lld",ans);}
[Apio2014]序列分割