Codeforces 311B
题意:有$m$只猫,放在一些位置$[1,n]$,每个位置之间的距离$d_i$,每个猫出现的时间$t_i$,假设派出$P$个人,可以自由安排其出发时间,沿途将已经出现的猫取掉,猫等待的时间是被取的时间减去出现的时间。现在有$P$个人,问总时间$T$最小是多少。
考虑设$a_i= t_i - \sum_\limits{j=1}^{h_i} d_i$,那么这个时间即为人什么时候出发,能取到猫$i$。
那么如果人出发时间为$g$,则该猫的等待时间为$a_i - g$(能取到的话)。
那么这样题目清楚了很多,如果$P=1$,那么答案就是最大值减去其他所有值的和。
考虑$p \leq 2$的情况:
我们可以发现这些猫现在的$a_i$和位置没有关系了,可以考虑将$a_i$按从小到大排序,然后发现只用用$P$个序列覆盖$a_i$即可。
这样还是麻烦,注意观察,序列一定是子串。因为不连续的一定不优,假设有$[1,3],[4,5],[6,8]$,则如果$[1,3], [6,8]$一个人,那么一定没有$[4, 5], [6,8]$一个人优。
所以可以开始DP:设$dp(i,j)$为分了$i$段,前$j$只猫的最小等待值,则
$$
dp(i,j)=\min_{1 \leq k \leq j} (dp(i-1,k)+a_j \cdot (j-k) - (\operatorname{sum}(j) - \operatorname{sum}(k)))
$$
直接枚举会炸。发现这里有$j,k$的乘积项,考虑斜率优化。
转化,得
$$
dp(i-1,k)+\operatorname{sum}(k)=dp(i,j)-a_j \cdot j + a_j \cdot k +\operatorname{sum}(j)
$$
那么决策可以写作点$(k, dp(i-1,k)+\operatorname{sum}(k))$
用这个算下斜率,即可斜率优化,注意开数组简化$dp(i-1,k)+\operatorname{sum}(k)$
并且一定要斜率大减小,避免负数对叉积的影响
知识点
1、斜率大减小,避免负数对叉积的影响
2、适当开数组简化一些东西
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<set>
#include<string>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db long double
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace flyinthesky {
const LL MAXN = 100000 + 5;
LL n, m, p, d[MAXN], h[MAXN], t[MAXN];
LL a[MAXN], sum[MAXN], dp[105][MAXN];
LL q[MAXN], g[MAXN];
void clean() {
ms(dp, 0), ms(sum, 0);
}
int solve() {
clean();
scanf("%lld%lld%lld", &n, &m, &p);
for (LL i = 2; i <= n; ++i) scanf("%lld", &d[i]), d[i] += d[i - 1];
for (LL i = 1; i <= m; ++i) scanf("%lld%lld", &h[i], &t[i]);
for (LL i = 1; i <= m; ++i) a[i] = t[i] - d[h[i]];
sort(a + 1, a + 1 + m);
for (LL i = 1; i <= m; ++i) sum[i] = sum[i - 1] + a[i];
ms(dp, 0x3f);
dp[0][0] = 0;
for (LL i = 1; i <= p; ++i) {
for (LL j = 1; j <= m; ++j) g[j] = dp[i - 1][j] + sum[j]; // 简化
LL l = 1, r = 1;
q[1] = 0;
for (LL j = 1; j <= m; ++j) {
while (l < r && (g[q[l + 1]] - g[q[l]]) <= a[j] * (q[l + 1] - q[l])) ++l; // 斜率大减小,避免负数对叉积的影响
dp[i][j] = min(dp[i - 1][j], g[q[l]] + a[j] * (j - q[l]) - sum[j]);
if (g[j] >= 0x3f3f3f3f3f3f3f3fll) continue ; // dp[i - 1][j] 为非法状态
while (l < r && (g[q[r]] - g[q[r - 1]]) * (j - q[r]) >= (g[j] - g[q[r]]) * (q[r] - q[r - 1])) --r; // 斜率大减小,避免负数对叉积的影响
q[++r] = j;
}
}
printf("%lld\n", dp[p][m]);
return 0;
}
}
int main() {
flyinthesky::solve();
return 0;
}