Loading

P3628 [APIO2010]特别行动队 单调队列优化dp

P3628 [APIO2010]特别行动队

链接

转移方程不难写出:设 \(f_i\) 表示把前 \(i\) 个士兵分成若干组的最优值。

那么转移就是:

\[f_{i}=\max\limits_{0\le k\le i-1}\{f_k+a\times (sum_i-sum_k)^2+b\times (sum_i-sum_k)+c \} \]

拆开后移项变成了:

\[f_k+a\times sum_k^2-b\times sum_k=2a\times sum_i\times sum_k+f_i-a\times sum_i^2-b\times sum_i-c \]

这就是一个 \(y=kx+b\) 的形式,注意到因为 \(a<0\) 所以斜率单调递减,所以可以用单调队列优化。

注意:

  • 写方程式不要错写漏写
  • 注意关注数据范围
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N 1000010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int n,x[N],sum[N],q[N],l,r,a,b,c,f[N];

inline dd calc_k_1(int k){
    return 2*a*sum[k];
}

inline dd calc_x(int k){
    return sum[k];
}

inline dd calc_y(int k){
    return f[k]+a*sum[k]*sum[k]-b*sum[k];
}

inline dd calc_k_2(int k1,int k2){
    dd x1=calc_x(k1),x2=calc_x(k2);
    dd y1=calc_y(k1),y2=calc_y(k2);
    // printf("%lf %lf %lf %lf\n",x1,y1,x2,y2);
    return (y1-y2)/(x1-x2);
}

signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(a);read(b);read(c);
    for(int i=1;i<=n;i++){
        read(x[i]);
        sum[i]=sum[i-1]+x[i];
    }
    q[++r]=0;
    for(int i=1;i<=n;i++){
        // while(top>=2&&calc_k_2(sta[top],sta[top-1])<calc_k_1(i)) top--;
        while(l<r-1&&calc_k_2(q[l+1],q[l+2])>calc_k_1(i)){
            // printf("l:%lld %lf %lf\n",l+1,calc_k_2(q[l+1],q[l+2]),calc_k_1(i));
            l++;
        }
        if(l<r){
            int now=q[l+1];
            // printf("i:%d now:%d\n",i,now);
            // printf("head:%lld\n",l+1);
            f[i]=f[now]+a*(sum[i]-sum[now])*(sum[i]-sum[now])+b*(sum[i]-sum[now])+c;
        }
        // while(top>=2&&calc_k_2(sta[top-1],sta[top])<calc_k_2(sta[top],i)) top--;
        // sta[++top]=i;
        while(l<r-1&&calc_k_2(q[r-1],q[r])<=calc_k_2(q[r],i)) r--;
        q[++r]=i;
    }
    // for(int i=1;i<=n;i++) printf("i:%d f:%d\n",i,f[i]);
    printf("%lld",f[n]);
    return 0;
}
posted @ 2021-07-03 09:33  hyl天梦  阅读(36)  评论(0编辑  收藏  举报