bzoj 1911 特别行动队

 

题意:将 n 个人分组,分组后,一个组的战斗力等于 a*sum*sum + b*sum + c,怎么分组使得战斗力和最大。

分析:

第一次自己从头到尾推出来的斜率DP。

状态定义 d[i] : 前 i 个人分组得到的最优值。

状态转移 d[i] =  max (  d[j]  + a*(sum[i] - sum[j])^2 +b*(sum[i] - sum[j])  +c   )

显然是O(n^2)

 

假设 k < j < i ; j 决策点更优。

 

 这个斜率公式比较复杂了,其实就是将分子,分母转为 与 j 和 k 有关的斜率式子,另一边不能有关于 j 和 k 。

有了这个斜率公式,接下来就是分析斜率递增还是递减了。

 

假设3个决策点 k < j < i

显然 凸多边形,j 点不是最优点,不可能存在凸点。

然后就是根据多边形的性质,相切之前怎么样,加入之后删点等... ...

#include<bits/stdc++.h>

using namespace std;
const int maxn = 1e6+5;

int n;
long long a,b,c;
long long x;
long long sum[maxn];
long long d[maxn];
double slope(int i,int j)
{
    double up = d[i]-d[j]+a*(sum[i]*sum[i]-sum[j]*sum[j])+b*(sum[j]-sum[i]);
    double down = 2*a*(sum[i]-sum[j]);
    return up/down;
}
int l,r,q[maxn];

int main()
{
    scanf("%d",&n);
    scanf("%lld%lld%lld",&a,&b,&c);
    long long x;
    for(int i = 1; i <= n; i++) {

        scanf("%lld",&x);
        sum[i] = sum[i-1] + x;
    }

    for(int i=1;i<=n;i++)
    {
        while(l<r&&slope(q[l],q[l+1])<sum[i])l++;
        int now = q[l];
        d[i]=d[now]+a*(sum[i]-sum[now])*(sum[i]-sum[now])+b*(sum[i]-sum[now])+c;
        while(l<r&&slope(q[r-1],q[r])>slope(q[r],i))r--;
        q[++r]=i;
    }
    printf("%lld\n",d[n]);

    return 0;
}
View Code

 

posted @ 2017-09-06 21:45  小草的大树梦  阅读(97)  评论(0编辑  收藏  举报