题解 P3628 【[APIO2010]特别行动队】

Si=j=1iaiS_i=\sum_{j=1}^ia_i

我们可以先列一个简单的dp方程:

f(i)=minj=1i1{A(SiSj)2+B(SiSj)+C}f(i)=\min_{j=1}^{i-1} \{A(S_i-S_j)^2+B(S_i-S_j)+C\}

先去掉minmin符号,然后整理:

2ASiSjASi2BSiC+f(i)=f(j)+ASj2BSj2AS_iS_j-AS_i^2-BS_i-C+f(i)=f(j)+AS_j^2-BS_j

我们把所有与ii有关的全部项移到左边,其余移到右边。并且以jj为主元,把jjSjS_j等看作变量,其余看作常数。然后我们确保2ASi2AS_i为正数。(为了方便,我们把AABBCC均乘以1-1,然后求f(i)f(i)的最小值)

我们让一条斜率为2ASi2AS_i的直线依次经过这i1i-1个点,然后找到使它在yy轴上的截距最小的点即可。

注意到当直线Pj1PjP_{j-1}P_j的斜率大于PjPj+1P_jP_{j+1}时,PjP_j一定不是我们要找的点,所以我们可以用单调队列来维护所有的决策点,每次插入一个新决策点时,先检查它与队首构成的直线的斜率,以及队首的两个点构成的直线的斜率,如果不对就把队首扔掉,并继续。(详见代码)

这个不对

这个对

并且,如果Pj1PjP_{j-1}Pj的斜率大于2ASi2AS_i,说明Pj1P_{j-1}优于PjP_j。如果PjPj+1P_jP_{j+1}的斜率小于2ASi2AS_i,说明Pj+1P_{j+1}优于PjP_j。因此,如果有三个点Pj1P_{j-1},PjP_j,Pj+1P_{j+1},满足KPj1Pj<2ASiKPjPj+1K_{P_{j-1}P_j}<2AS_i\leq K_{P_jP{j+1}},那么PjP_j就是最优决策点。在本题中,由于2ASi2AS_i单调递增,所以若PjPj+1P_jP_{j+1}的斜率小于2ASi2AS_i,就可以把PjP_j扔掉,这样队尾永远是当前最优的决策点。但是,在某些问题中,斜率并不是单调递增的,这时不能把队尾扔掉,而是每次要使用二分查找的技巧,找到第一条斜率不小于2ASi2AS_i的直线。

然后就看代码吧:

#include <bits/stdc++.h>
#define debug printf("Running %s on line %d...\n",__FUNCTION__,__LINE__)
#define in inline
#define re register
#define sqr(i) (ll((i))*(i))
#define F(i) (A*sqr(s[(i)])-B*s[(i)]+f[(i)])
using namespace std;
typedef long long ll;
typedef double db;
in int read()
{
    int ans=0,f=1;char c=getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    for (;isdigit(c);c=getchar()) ans=(ans<<3)+(ans<<1)+(c^48);
    return ans*f;
}
int a[1000005],s[1000005];
int q[1000005],l,r;
ll f[1000005];
ll A,B,C;
// f(i)=f(j)+A(s[i]-s[j])^2+B(s[i]-s[j])+C
//     =f(j)+A*s[i]^2-2As[i]s[j]+As[j]^2+Bs[i]-Bs[j]+C
// 2As[i]s[j]-As[i]^2-Bs[i]-C+f(i)=f(j)+As[j]^2-Bs[j]
int main()
{
    int n=read();
    A=-read(),B=-read(),C=-read();
    for (int i=1;i<=n;i++) a[i]=read(),s[i]=s[i-1]+a[i];
    l=r=1;
    for (int i=1;i<=n;i++)
    {
        while (r>l && (F(q[l+1])-F(q[l]))<2*A*s[i]*(s[q[l+1]]-s[q[l]])) l++;
        f[i]=f[q[l]]+A*sqr(s[i]-s[q[l]])+B*(s[i]-s[q[l]])+C;
        while (r>l && (F(q[r])-F(q[r-1]))*(s[i]-s[q[r]])>(F(i)-F(q[r]))*(s[q[r]]-s[q[r-1]])) r--;
        q[++r]=i;
    }
    cout<<-f[n]<<endl;
    return 0;
}