BZOJ 1911 (特别行动队)
题意:给定一个数列,将其分成若干段,若某段的和为x则这段的价值为a*x*x+b*x+c。求一种分法使得总价值最大
f[i]为到第i个时的最大价值,
f[i]=max(f[j]+a*(sum[i]-sum[j])^2+b*(sum[i]-sum[j])+c
若j1<j2且j2更优
f[j1]+a*sum[i]^2+a*sum[j1]^2-2*a*sum[i]*sum[j1]+b*sum[i]-b*sum[j1]+c<f[j2]+a*sum[i]^2+a*sum[j2]^2-2*a*sum[i]*sum[j2]+b*sum[i]-b*sum[j2]+c
最终化简得到
f[j1]-f[j2]+a*sum[j1]^2-a*sum[j2]^2-b*sum[j1]+b*sum[j2]<2*a*sum[i]*(sum[j1]-sum[j2])
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<string> 6 #include<algorithm> 7 int n; 8 long long sum[1000005],a,b,c,x,q[1000005],f[1000005]; 9 long long sqr(long long x){ 10 return x*x; 11 } 12 double slope(int k,int j){ 13 return double((f[j]-f[k])+a*sqr(sum[j])-a*sqr(sum[k])+b*(sum[k]-sum[j]))/(2*a*(sum[j]-sum[k])); 14 } 15 16 int main(){ 17 scanf("%d",&n); 18 scanf("%lld%lld%lld",&a,&b,&c); 19 sum[0]=0; 20 for (int i=1;i<=n;i++){ 21 scanf("%lld",&x); 22 sum[i]=sum[i-1]+x; 23 } 24 int l=0,r=0; 25 for (int i=1;i<=n;i++){ 26 while (l<r&&slope(q[l],q[l+1])<sum[i]) l++; 27 int t=q[l]; 28 f[i]=f[t]+a*(sum[i]-sum[t])*(sum[i]-sum[t])+b*(sum[i]-sum[t])+c; 29 while (l<r&&slope(q[r-1],q[r])>slope(q[r],i)) r--; 30 q[++r]=i; 31 } 32 printf("%lld",f[n]); 33 }