BZOJ1010 玩具装箱toy
斜率优化dp。首先比较容易想到:
但是很不幸,由上式的可以看出这实际上要用到两层循环,而数据量为50000,如果这样做,肯定超时,这时候需要斜率优化了。
如果递推式能变成,且单调,则可使用斜率优化。
我们将式变形,令,则
由此判断可以使用斜率优化。
关键的来了:
如果我们认为的两个方案且的方案的方案好则:
$dp[j]+(g[i]-g[j]-c)^2\leq dp[k]+(g[i]-g[k]-c)^2$
$\frac{dp[j]+g[j]^2+2*g[j]*c-dp[k]-g[k]^2-2*g[k]*c}{g[j]-g[k]} > g[i]$
然后将令,可以看出这个分式实际上就表示斜率,由这个斜率我们可以得出,如果的方案的方案好,则必须满足式,所以我们现在只需要将最有方案放在一个队列里,然后维护这个队列就行了,也就是凸包的维护。
我讲下我个人当时最难理解的地方,为什么最优解在凸包上。
如上图所示,不在凸包上有两种情况,一种是F,一种是D。如果F在队列里,很明显,斜率为负的,不符合,排除,如果D在队列里,CD的斜率小于CB斜率,可得CB最优,所以排除D。
然后是凸包的维护方法。
首先是取。如果时,是由转移而来,因为如果不满足不等式,说明方案并不是最有的,直接剔除,如果满足不等式,则当前这个点就是最优解,因为由函数知道,优于。
然后是加。如果$slope(q[back],i)<slope(q[back-1],q[back])$,也就是加入变成了凹的,我们需要将出列,直到不等式不成立,再将加到队列中。
code:
#define frp #include<bits/stdc++.h> #include <algorithm> #include <cmath> #include <iostream> #include <cstring> #include <string> #include <string.h> using namespace std; typedef long long ll; const ll INF = 0x3f3f3f3f; const ll inf = 0x7fffff; const int maxn = 1e6; const int MAXN = 1100000 + 10; const int MOD = 1e9 + 7; ll dp[maxn],sum[maxn],q[maxn]; int c,l; double slope(int i,int j){ return (dp[i]+sum[i]*sum[i]+2*sum[i]*c-dp[j]-sum[j]*sum[j]-2*sum[j]*c)/(2.0*(sum[i]-sum[j])); } void solve() { int n; cin>>n>>l; c=l+1; for(int i=1;i<n+1;i++){ ll tmp; cin>>tmp; sum[i]=sum[i-1]+tmp; } for(ll i=1;i<n+1;i++)sum[i]+=i; int front=1,back=1; q[back]=0; for(int i=1;i<n+1;i++){ while(front<back&&slope(q[front],q[front+1])<=sum[i])front++; int w=q[front]; dp[i]=dp[w]+(sum[i]-sum[w]-c)*(sum[i]-sum[w]-c); while(front<back&&slope(q[back],i)<slope(q[back-1],q[back]))back--; q[++back]=i; } cout<<dp[n]<<endl; } int main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); #ifdef frp freopen("D:\\coding\\c_coding\\in.txt", "r", stdin); // freopen("D:\\coding\\c_coding\\out.txt", "w", stdout); #endif solve(); return 0; }