[Bzoj1010][HNOI2008]玩具裝箱toy

題目連接:https://www.lydsy.com/JudgeOnline/problem.php?id=1010php

首先預處理前綴和sum[i],而後推出初始的dp方程,dp[i]表示裝前i件物品的最小費用,v=sum[i]-sum[j]+i-j-1,dp[i]=min(dp[i],dp[j]+(v-l)2)(j<i)。複雜度是O(n2),實測會被卡,這裏要斜率優化下,設i>j>k時,由第j項轉移到第i項比從第k項轉移更優。則有dp[j]+(sum[i]-sum[j]+i-j-1-l)2)<dp[k]+(sum[i]-sum[k]+i-k-1-l)2),展開移項:
(dp[j] + sum[j]*sum[j] + 2*j*sum[j] + 2*sum[j] + j*j + 2*l*sum[j] + 2*l*j + 2*j-(dp[k]+sum[k]*sum[k] + 2*k*sum[k] + 2*sum[k] +k*k + 2*l*sum[k] + 2*l*k+2*k))/(sum[j]+j-(sum[k]+k))<=2*(sum[i]+i);
設f[i]=dp[i] + sum[i]*sum[i] + 2*i*sum[i] + 2*sum[i] + i*i + 2*l*sum[i] + 2*l*i + 2*i。
設T[i]=sum[i]+i。
則上面的式子化爲(f[j]-f[k])/(T[j]-T[k])<=2T[i]
則當每一個點的dp值被求出時,能夠在二維座標系中獲得座標(T[i],f[i])。
用單調隊列維護一個下凸包點集,便可O(1)獲得最優勢。
注意使用long longios

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 typedef long long ll; 7 const double eps = 1e-8; 8 const ll INF = 9e18 + 7; 9 const int maxn = 5e5 + 10; 10 ll dp[maxn]; 11 ll q[maxn]; 12 ll p[maxn]; 13 ll sum[maxn]; 14 ll n, l; 15 long long check(int j, int k) { 16 return dp[j] + sum[j] * sum[j] + 2 * j*sum[j] + 2 * sum[j] + j * j + 2 * l*sum[j] + 2 * l*j + 2 * j - dp[k] - sum[k] * sum[k] - 2 * k*sum[k] - 2 * sum[k] - k * k - 2 * l*sum[k] - 2 * l*k - 2 * k; 17 } 18 int main() { 19 while (scanf("%lld%lld", &n, &l) != EOF) { 20 for (int i = 1; i <= n; i++) 21 scanf("%lld", &p[i]), sum[i] = sum[i - 1] + p[i]; 22 ll le = 1, re = 1; 23 q[le] = 0; 24 for (int i = 1; i <= n; i++) { 25 while (le < re&&check(q[le + 1], q[le]) <= (2 * sum[i] + 2 * i)*(sum[q[le + 1]] + q[le + 1] - sum[q[le]] - q[le]))le++; 26 int ans = q[le]; 27 ll v = sum[i] - sum[ans] + i - ans - 1; 28 dp[i] = dp[ans] + (v - l)*(v - l); 29 while (le < re&&check(q[re], q[re - 1])*(sum[i] + i - sum[q[re]] - q[re]) >= check(i, q[re])*(sum[q[re]] + q[re] - sum[q[re - 1]] - q[re - 1]))re--; 30 q[++re] = i; 31  } 32 printf("%lld\n", dp[n]); 33  } 34 }
相關文章
相關標籤/搜索