想了好久啊.。。。——黑字为第一次更新。——这里是第二次更新,维护上下凸包据题而论,第一种方法是化式子的方法,需要好的化式子的方法,第二种是偏向几何,十分好想,纯正的维护凸包的方法,推荐。
用了我感觉比较好写的一种(因为没写过维护凸包),另一种是维护(上)凸包的做法,本质一样?推荐http://www.mamicode.com/info-detail-345781.html。
网上的大多数解法:
DP:f[i]=max(f[j]+a*(sum[i]-sum[j])^2+b(sum[i]-sum[j])+c)
显然复杂度不对。
那么假设j>k且f[j]优于f[k]
f[j]-f[k]+a*(sum[j]^2-sum[k]^2)-b*(sum[j]-sum[k])>2*a*(sum[x]-sum[y])*sum[i] (过程省略,把已知的sum[i]放在一边,剩下的放在一边)
其实不太明白这个式子求的是啥,但是可以感受到其中的单调之力(用里面的单调函数应该能科学的证明),我理解为“优度”,优度>sum[i]时就是表示j>k且f[j]优于f[k]的时候,也就是用单调队列维护这个“优度”。(以上为强行YY出的解释)
于是就这样了。。。注意:a<0,除过来要变号(mdzz)
1 #include <iostream> 2 #include <cstdio> 3 #define N 1000000+100 4 #define ll long long 5 using namespace std; 6 ll sum[N],f[N]; 7 int l,r,n,a,b,c,x; 8 int q[N]; 9 inline int read() 10 { 11 int ans=0,f=1; 12 char c; 13 while (!isdigit(c=getchar())) if (c=='-') f=-1; 14 ans=c-'0'; 15 while (isdigit(c=getchar())) ans=ans*10+c-'0'; 16 return ans*f; 17 } 18 inline ll Pow(ll x) {return x*x;} 19 inline double Getk(int x,int y) {return (double)(f[x]-f[y]+a*(Pow(sum[x])-Pow(sum[y]))-b*(sum[x]-sum[y]))/(double)(2*a*(sum[x]-sum[y]));} 20 int main() 21 { 22 n=read(); 23 a=read(); b=read(); c=read(); 24 for (int i=1;i<=n;i++) x=read(),sum[i]=sum[i-1]+x; 25 for (int i=1;i<=n;i++) 26 { 27 while (l<r && Getk(q[l+1],q[l])<sum[i]) l++; 28 int p=q[l]; 29 f[i]=f[p]+a*Pow((sum[i]-sum[p]))+b*(sum[i]-sum[p])+c; 30 while (l<r && Getk(i,q[r])<Getk(q[r],q[r-1])) r--; 31 q[++r]=i; 32 } 33 printf("%lld",f[n]); 34 return 0; 35 }