[bzoj1911]特别行动队

来源:互联网 发布:天刀彭于晏捏脸数据 编辑:程序博客网 时间:2024/04/28 04:10

先上题目

这里写图片描述

输入描述

这里写图片描述

输出描述

这里写图片描述

数据范围

这里写图片描述


设 F(x)=ax^2+bx+c
我们可以很容易地列出一个状态转移方程:

f[i]=max(f[j]+F(sum[i]-sum[j]))  (i<j)

最原始的方程我们算是列出来了,然而如果直接转移,O(n^2)的时间复杂度我们是无法承受的,所以我们考虑优化

对方程进行化简,则有

f[i]=f[j]+a*sum[i]^2-2a*sum[i]*sum[j]+a*sum[j]^2+b*sum[i]-b*sum[j]+c

按照含i或j进行整理,则

f[j]+a*sum[j]^2-b*sum[j]=2a*sum[i]*sum[j]+f[i]-a*sum[i]^2-b*sum[i]-c

如果将其用 y=kx+b 的形式表示则有

y=f[j]+a*sum[j]^2-b*sum[j]k=2a*sum[i]x=sum[j]b=f[i]-a*sum[i]^2-b*sum[i]-c

这样表示的优势就一目了然了,因为此时每一个j都能对应一个(x,y)。于是我们便可以将所求得的(x,y)拿到坐标轴上表示。
同时,在每一个x,y下,我们就可以利用对应的k计算出相应的b。而通过b,我们就可以算出f[i]的值

在图像中,b为 y=kx+b 的截距。显然,f[i]是随b的递增而递增的,所以,如果我们能保证直线的截距最大,那么f[i]就是最优的。

随后我们只需要维护一个凸包,将斜率为k的直线从无限高处往下平移,接触到的第一个凸包上的点显然能保证此时直线的截距最大。

我们不难发现,此时的斜率k是具有单调性的,我们就可以用单调队列对凸包进行维护。一旦队列头的一点对于i处理出来的斜率k劣于队列中的第二个点,就把头出队,这样出队操作完成后就可以取队头为i的最优决策j了。

所以,这题就差不多这样了。。。

上代码

#include<iostream>#include<cstdio>#include<algorithm>using namespace std;#define MAXN 1000000+10int n,l,r;int a,b,c;int  x[MAXN];long long sum[MAXN],f[MAXN];int q[MAXN];long long sqr(long long  x){return x*x;}inline double slop(int k,int j){return (double)(f[j]-f[k]+a*(sqr(sum[j])-sqr(sum[k]))+b*(sum[k]-sum[j]))/(double)(2*a*(sum[j]-sum[k]));}int main(){    scanf("%d%d%d%d",&n,&a,&b,&c);    for(int i=1;i<=n;i++)scanf("%d",&x[i]);    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+x[i];    for(int i=1;i<=n;i++){        while(l<r&&slop(q[l],q[l+1])<sum[i])l++;        int t=q[l];        f[i]=f[t]+a*sqr(sum[i]-sum[t])+b*(sum[i]-sum[t])+c;        while(l<r&&slop(q[r-1],q[r])>slop(q[r],i))r--;        q[++r]=i;    }    printf("%lld",f[n]);    return 0;}
0 0
原创粉丝点击