BZOJ 3675: [Apio2014]序列分割
Description
将一个序列切割\(k\)次,每次切割的收益是两边和的乘积,求最大收益.\(n\leqslant 1\times 10^5,k\leqslant 200\)
Solution
斜率优化DP..
因为什么\((a+b)c+ab=a(b+c)+bc\)..
所以他是只与结果有关的,跟切割方法无关,随便切就行...
\(f[i][j]=max\{f[k][j-1]+s[k](s[i]-s[k])\}\)
斜率正好是前缀和,并且单调不降..
所以直接斜率优化那一套就行了...
Code
/************************************************************** Problem: 3675 User: BeiYu Language: C++ Result: Accepted Time:14596 ms Memory:84112 kb ****************************************************************/ #include <bits/stdc++.h> using namespace std; typedef long long LL; const int N = 100005; const int M = 205; inline int in(int x=0,char ch=getchar()) { while(ch>'9'||ch<'0') ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();return x; } int n,k; int a[N],q[M][N],h[M],t[M]; LL s[N],f[N][2]; //#define B(i,j) (f[i][(j)&1]-s[i]*s[i]) //#define F(k,i,j) (B(k,((j)^1))+s[k]*s[i]) inline LL B(int i,int j) { return f[i][j&1]-s[i]*s[i]; } inline LL F(int k,int i,int j) { return B(k,j^1)+s[k]*s[i]; } int main() { // cout<<(sizeof(f)+sizeof(q)+sizeof(s)+sizeof(a))/1024.0/1024.0<<endl; n=in(),k=in();k++; for(int i=1;i<=n;i++) a[i]=in(); for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i]; for(int j=1;j<=k;j++) h[j]=1; // q[0][h[0]=t[0]=1]=0; // for(int i=1;i<=n;i++) f[i][1]=s[i]; for(int j=2;j<=k;j++) { q[j-1][h[j-1]=t[j-1]=1]=j-1; for(int i=j;i<=n;i++) { while(h[j-1]<t[j-1] && F(q[j-1][h[j-1]],i,j)<F(q[j-1][h[j-1]+1],i,j)) h[j-1]++; f[i][j&1]=F(q[j-1][h[j-1]],i,j); // cout<<i<<" "<<j<<" "<<q[j-1][h[j-1]]<<endl; while(h[j-1]<t[j-1] && (s[q[j-1][t[j-1]]]-s[q[j-1][t[j-1]-1]])*(B(q[j-1][t[j-1]-1],j-1)-B(i,j-1)) <=(s[i]-s[q[j-1][t[j-1]-1]])*(B(q[j-1][t[j-1]-1],j-1)-B(q[j-1][t[j-1]],j-1))) t[j-1]--; q[j-1][++t[j-1]]=i; } } // for(int j=0;j<=1;j++) for(int i=1;i<=n;i++) printf("%lld%c",f[i][j]," \n"[i==n]); printf("%lld\n",f[n][k&1]); return 0; }