×

第十一篇:P2120

题解补完计划:第十一篇
(今天也是在模拟赛过程写上题解了(什么lj题目,为什么会存在于现实)(为什么正解过不了/fn))。

原题题面

显然,这道题是 DP,我们令 \(dp_i\) 表示前 \(i\) 个点的所有货物都有仓库去的最小代价。显然,我们可以写出下面这个式子。
\(\begin{aligned} dp_i &= min_j^i dp_j + \sum_{k=j+1}^i (x_i - x_k) \times p_k \\ &= min_j^i dp_j + x_i\sum_{k=j+1}^ip_k - \sum_{k=j+1}^i p_k\times x_k \end{aligned}\)

我们令 \(S_n = \sum_{i=1}^n x_i\times p_i, P_n = \sum_{i=1}^n p_i\) 。那么就可以转化为下面这个式子。

\[ dp_i = min_j^i dp_j + x_i\times(P_i - P_j) - S_i + S_j \]

我们发现出现了交叉项,各项单调不降,故考虑斜率优化,我们考虑 \(j_1 < j_2\) 且 \(j_1\) 更优,有。

\[ dp_{j_1} + x_i\times(P_i-P_{j_1}) - S_i + S_{j_1} < dp_{j_2} + x_i\times(P_i-P_{j_2}) - S_i + S_{j_2} \]

整理得:

\[ \frac{dp_{j_1}+S_{j_1} - (dp_{j_2} + S_{j_2})}{P_{j_1}-P_{j_2}} > x_i \]

因为 \(x_i\) 单调递增,所以我们维护一个 \((P, dp+S)\) 的下凸包就可以了,时间复杂度 \(O(n)\)。

#include<bits/stdc++.h>
using namespace std;
#define N 1000005
#define intl long long
#define For(i,a,b) for(intl i=a;i<=b;i++)
#define deo(i,a,b) for(intl i=a;i>=b;i--)
intl read() {
	intl x=0,k=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch == '-') k=-1;ch=getchar();}
	while(isdigit(ch)) {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*k;
}
intl p[N],n,x[N],sum[N],c[N],dp[N],sta[N],top,q[N];

double K(intl j1,intl j2) {
	if(p[j1] == p[j2]) {
		if(dp[j1] + sum[j1] - dp[j2] - sum[j2] == 0) return 0;
		if(dp[j1] + sum[j1] - dp[j2] - sum[j2] > 0) return 1e18;
		return -1e18;
	}
	return 1.0*(dp[j1] + sum[j1] - dp[j2] - sum[j2])/(p[j1]-p[j2]);
}
int main() {
	n = read();
	For(i,1,n) x[i] = read(), p[i] = read(), c[i] = read();
	For(i,1,n) sum[i] = sum[i-1] + p[i]*x[i], p[i] += p[i-1];
	intl head = 1, tail = 1;
	For(i,1,n) {
		while(head < tail && K(q[head+1],q[head]) < x[i]) head ++;
		dp[i] = dp[q[head]] - sum[i] + sum[q[head]] + x[i]*(p[i] - p[q[head]]) + c[i];
		while(head < tail && K(i,q[tail-1]) < K(q[tail],q[tail-1])) tail--;
		q[++tail] = i;
	}
	intl tot = n, ans = dp[n];
	while(tot && p[tot] == p[n]) ans = min(ans,dp[tot]),tot--;
	printf("%lld\n",ans);
	return 0;
}


posted @ 2026-10-05 17:28  shiori123  阅读(4)  评论(0)    收藏  举报