洛谷3195(HNOI2008)玩具装箱

题目:https://www.luogu.org/problemnew/show/P3195

自己做斜率优化的第一道题。

推成斜率优化的样子很重要。

  斜率优化的样子就是从 j 中求 i 的话,关系式里一个量只和 i 有关,一个量只和 j 有关,一个量同时和 i 与 j 有关。

  这时可以把那个 同时和 i 与 j 有关的量 里的和 j 有关的量看成 x[ j ],把只和 j 有关的量看成 y[ j ],然后只和 i 有关的量就是截距、x[ j ]前面的就是式子里的斜率。

(为了推出这样的式子,可以设a,b等等,帮助自己推。大体思路是将与 i 或 j 或 i 和 j 有关的东西看成一个整体)

推出式子以后,找合适的 j 就是 j - 1 与 j 的斜率比式子里的斜率大(或小),而 j 与 j + 1 的斜率比式子里的斜率小(或大)的那个 j 。

找到 j 以后把式子变变形就得到推出dp[ i ] 的式子了。

可用单调队列。

把a什么的写成函数很方便。

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define db double
using namespace std;
const int N=50005;
int n,L,h=1,t=1,q[N];
ll s[N],dp[N];
db a(int i){return s[i]+i;}
db b(int i){return s[i]+i+L+1;}
db x(int i){return b(i);}
db y(int i){return b(i)*b(i)+dp[i];}
db slope(int j,int i){return (y(i)-y(j))/(x(i)-x(j));}
int main()
{
    scanf("%d%d",&n,&L);ll z;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&z);s[i]=s[i-1]+z;
    }
    for(int i=1;i<=n;i++)
    {
        while(h<t&&slope(q[h],q[h+1])<2*a(i))h++;
        dp[i]=(a(i)-b(q[h]))*(a(i)-b(q[h]))+dp[q[h]];
        while(t>h&&slope(q[t-1],q[t])>slope(q[t-1],i))t--;
        q[++t]=i;
    }
    printf("%lld",dp[n]);
    return 0;
}

 

posted on 2018-06-05 14:57  Narh  阅读(90)  评论(0编辑  收藏  举报

导航