第十一篇: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;
}

浙公网安备 33010602011771号