bzoj 1911: [Apio2010]特别行动队【斜率优化dp】
仔细想想好像没学过斜率优化..
很容易推出状态转移方程\( f[i]=max{f[j]+a(s[i]-s[j])^2+b(s[i]-s[j])+c} \)
然后考虑j的选取,如果选j优于选k,那么:
\[f[j]+a(s[i]-s[j])^2+b(s[i]-s[j])+c>f[k]+a(s[i]-s[k])^2+b(s[i]-s[k])+c
\]
\[f[j]+as[i]^2+as[j]^2-2as[i]s[j]+bs[i]-bs[j]+c>f[k]+as[i]^2+as[k]^2-2as[i]s[k]+bs[i]-bs[k]+c
\]
\[f[j]-f[k]+as[j]^2-as[k]^2-bs[j]+bs[k]>2as[i]s[j]-2as[i]s[k]
\]
\[f[j]-f[k]+a(s[j]^2-s[k]^2)-b(s[j]+s[k])>2as[i](s[j]-s[k])
\]
\[\frac{f[j]-f[k]+a(s[j]^2-s[k]^2)-b(s[j]+s[k])}{2a(s[j]-s[k])}>s[i]
\]
注意这里的2a一定要除到左边不然会WA...(可能是精度问题)
然后维护这样一个斜率单调的双端队列即可。
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1000005;
int n,a,b,c,d[N],q[N],l,r;
long long f[N],s[N];
int read()
{
int r=0,f=1;
char p=getchar();
while(p>'9'||p<'0')
{
if(p=='-')
f=-1;
p=getchar();
}
while(p>='0'&&p<='9')
{
r=r*10+p-48;
p=getchar();
}
return r*f;
}
inline double wk(int x,int y)
{
return (double)(f[y]-f[x]+a*(s[y]*s[y]-s[x]*s[x])+b*(s[x]-s[y]))/(double)(2*a*(s[y]-s[x]));
}
int main()
{
n=read(),a=read(),b=read(),c=read();
for(int i=1;i<=n;i++)
d[i]=read(),s[i]=s[i-1]+d[i];
for(int i=1;i<=n;i++)
{
while(l<r&&wk(q[l],q[l+1])<s[i])
l++;
f[i]=f[q[l]]+a*(s[i]-s[q[l]])*(s[i]-s[q[l]])+b*(s[i]-s[q[l]])+c;
while(l<r&&wk(q[r-1],q[r])>wk(q[r],i))
r--;
q[++r]=i;
}
printf("%lld\n",f[n]);
return 0;
}