BZOJ 1010 [HNOI2008]玩具装箱toy 斜率优化dp
斜率优化dp:
好久没有写斜率优化dp都忘记了这个东西到底是怎么回事。
对于斜率优化dp来说, 我们可以将一个 转移方程转换成 y = k x + b.
其中要求的东西在b上。
注意: 现在是给定了一堆点(x,y), 让你在固定k的前提下求出最小/最大的b是多少。
如果现在是维护最小的b。
那么我们需要维护出一个下凸壳。
如果k是递增的话, 我们可以不断的将队头不是最优的(x,y)去掉,留下最优的点作为对头。(讲道理,如果在求最小b的情况下, 这个k应该不会递减,不然就显得特别蠢,23333)。
如果k是随意的话,我们可以把下凸壳维护出来,然后二分找到最优解。
题解:
一开始列的方程应该为 dp[i] = min(dp[j] + (i-j-1 + sum[i] - sum[j] - L)^2)。
因为n=1e5,所以这个题目不能用n^2去转移。
可以令A = sum[i] + i, B = sum[j]+j+L+1
化简前面的式子,可得:dp[j] + B^2 = 2 * A * B + dp[i] - A^2.
y = dp[j] + B ^ 2, k = 2 * A, x = B, dp[i] - A ^ 2。
也就是上面说的y = k * x + b了。
通过维护一个下凸壳,我们可以在o(n)的复杂度内求出答案。
代码:
#include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const int _inf = 0xc0c0c0c0; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL _INF = 0xc0c0c0c0c0c0c0c0; const LL mod = (int)1e9+7; const int N = 2e5 + 100; int n, L; int a[N]; int sta[N]; LL sum[N]; LL dp[N]; LL calb(int id, int i){ LL B = sum[id] + id + L + 1; LL A = sum[i] + i; return dp[id] + B * B - 2 * A * B; } double slope(int x, int y){ double x1 = sum[x] + x + L + 1; double y1 = dp[x] + x1 * x1; double x2 = sum[y] + y + L + 1; double y2 = dp[y] + x2 * x2; return (y1-y2) / (x1-x2); } int Ac(){ scanf("%d%d", &n, &L); for(int i = 1; i <= n; ++i){ scanf("%d", &a[i]); sum[i] = sum[i - 1] + a[i]; } int L = 1, R = 1; sta[0] = 0; for(int i = 1; i <= n; ++i){ while(L < R && calb(sta[L], i) > calb(sta[L+1], i)) ++L; LL A = sum[i] + i; dp[i] = calb(sta[L], i) + A * A; while(L < R && slope(i,sta[R]) < slope(sta[R], sta[R-1])) --R; sta[++R] = i; } printf("%lld\n", dp[n]); return 0; } int main(){ Ac(); return 0; }