[斜率优化]洛谷 P3195 玩具装箱 题解

斜率优化板子

以下的 \(sum\) 代表前缀和,不难发现我们其实要求的就是下面这个东西

\[dp_i = \min(dp_j + (sum_i - sum_j + i - j - 1 - L) ^ 2) \]

这里其实有一个技巧的。作者第一次做这个题的时候是给括号全打开了,化出来了一大堆常数。但是实际上完全算不对,而且没必要。

我们可以单独整两个函数

\[a(x) = sum_x + x \]

\[b(x) = sum_x + x + L + 1 \]

这样就简单啦!原式等于(\(\min\) 函数省略)

\[dp_i = dp_j + (a(i) - b(j)) ^ 2 \]

\[dp_i = dp_j + a(i) ^ 2 - 2 a(i) b(j) + b(j) ^ 2 \]

然后搞一个斜率优化就行

\[dp_j + b(j) ^ 2 = 2 a(i)b(j) - a(i) ^ 2 + dp_i \]

符合 \(y = kx + b\) 的形式

其中 \(y = dp_j + b(j) ^ 2\)

\(k = 2a(i)\)

\(x = b(j)\)

\(b = -a(i)^2 + dp_i\)

然后这题就完了

#include <bits/stdc++.h>

#define qwq return 0
#define N 500007
#define int long long

int h , t , n , L , c[N] , dp[N] , q[N];
namespace P3195 {
	using namespace std;
	
	inline int a(int x) {
		return c[x] + x;
	}
	
	inline int b(int x) {
		return a(x) + L + 1;
	}
	
	inline double slope(int i , int j) {
		return (dp[i] + b(i) * b(i) - dp[j] - b(j) * b(j)) * 1.0 / (b(i) - b(j));
	}
	
	void Kx() {
		ios:: sync_with_stdio(0) , cin.tie(NULL) , cout.tie(NULL);
		cin >> n >> L;
		for(register int i = 1; i <= n; ++i) {
			cin >> c[i]; c[i] += c[i - 1];
		}
		
		h = 1 , t = 0;
		for(register int i = 1; i <= n; ++i) {
			while(h < t && slope(i - 1 , q[t]) <= slope(q[t - 1] , q[t])) {
				--t;
			}
			q[++t] = i - 1;
			while(h < t && slope(q[h] , q[h + 1]) <= 2 * a(i)) {
				++h;
			}
			int target = q[h];
			dp[i] = dp[target] + (a(i) - b(target)) * (a(i) - b(target));
		}
		cout << dp[n];
	}
	
}
signed main() {
	P3195 :: Kx();
	qwq;
}
posted @ 2025-03-26 23:16  「癔症」  阅读(34)  评论(0)    收藏  举报