BZOJ P1911 [APIO2010] 特别行动队【动态规划】【斜率优化】

f [ i ] 表示只考虑前 i 个数的最大值, S u m [ ] 表示前缀和, F ( x ) = a x 2 + b x + c 显然:

f [ i ] = m a x 1 j i 1     f [ j ] + F ( S u m [ i ] S u m [ j ] )

f [ i ] = m a x 1 j i 1     a S u m [ i ] 2 + b S u m [ i ] + c + f [ j ] + a S u m [ j ] 2 b S u m [ j ] 2 a S u m [ i ] S u m [ j ]

假设 j < k ,且 j > i k > i 更优,那么易得:

a S u m [ j ] 2 b S u m [ j ] 2 a S u m [ i ] S u m [ j ] > a S u m [ k ] 2 b S u m [ k ] 2 a S u m [ i ] [ k ]

u j = f [ j ] + a S u m [ j ] 2 b S u m [ j ] 化简得:

2 a S u m [ i ] > u k u j S u m [ k ] S u m [ j ]

所以我们得到:当 j < k 时, j > i k > i 更优等价于 j 号点和 k 号点之间的斜率小于 2 a S u m [ i ]

同理得到 j > k 时的等价条件,此时我们用单调队列维护即可。

参考代码:

#include<cstdio>
#define LL long long
const LL Max=1e6+5;
struct Node{
    LL X,Y;
}Q[Max];
LL N,A,B,C,Left,Right,X[Max];
LL Cross(Node A,Node B,Node C){
    return (B.X-A.X)*(C.Y-A.Y)-(B.Y-A.Y)*(C.X-A.X);
}
int main(){
    LL I,J,K;
    scanf("%lld%lld%lld%lld",&N,&A,&B,&C);
    for(I=1;I<=N;I++){
        scanf("%lld",&X[I]);X[I]+=X[I-1];
    }
    for(I=1;I<=N;I++){
        while(Left<Right&&Q[Left].Y-2*A*X[I]*Q[Left].X<=Q[Left+1].Y-2*A*X[I]*Q[Left+1].X){
            Left++;
        }
        Node Cur;
        Cur.X=X[I];Cur.Y=Q[Left].Y-2*A*X[I]*Q[Left].X+2*A*X[I]*X[I]+C;
        while(Left<Right&&Cross(Q[Right-1],Cur,Q[Right])<=0){
            Right--;
        }
        Q[++Right]=Cur;
    }
    printf("%lld",Q[Right].Y+B*X[N]-A*X[N]*X[N]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yanzhenhuai/article/details/81200456