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;
}

浙公网安备 33010602011771号