洛谷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; }