题面
解法
- 显然有一个 O ( n 2 ) O(n^2) O(n2)的dp: f [ i ] = m i n ( f [ j − 1 ] + m a x ( h [ j ] , … , h [ i ] ) ) ( s [ i ] − s [ j − 1 ] ≤ L ) f[i] =min(f[j-1]+max(h[j],\dots,h[i]))\ (s[i]-s[j-1]\leq L) f[i]=min(f[j−1]+max(h[j],…,h[i])) (s[i]−s[j−1]≤L)。
- 那么我们考虑如何优化这个dp。
- 设 g [ j ] = m a x ( h [ j ] , … , h [ i ] ) g[j]=max(h[j],\dots,h[i]) g[j]=max(h[j],…,h[i]),那么显然可以发现 j j j在满足要求的区间中 g [ j ] g[j] g[j]单调不升。
- 那么我们不妨可以维护出 g [ j ] g[j] g[j]相同的区间是什么。显然可以发现,在 g [ j ] g[j] g[j]相同的区间 [ l , r ] [l,r] [l,r]中, f [ l ] f[l] f[l]一定是整个区间中 f f f最小的,那么我们可以直接使用 f [ i ] f[i] f[i]转移。
- 因为 f f f在不同的区间中并不一定单调,所以我们需要使用一个multiset来维护这些区间中的答案。
- 考虑如何维护这些区间,显然我们只要使用一个队列就可以实现。
- 时间复杂度: O ( n log n ) O(n\log n) O(nlogn)
【注意事项】
- 同时,如果状态转移方程写成 f [ i ] = m i n ( f [ j ] + m a x ( h [ j + 1 ] , … , h [ i ] ) ) ( s [ i ] − s [ j ] ≤ L ) f[i]=min(f[j]+max(h[j+1],\dots,h[i]))\ (s[i]-s[j]\leq L) f[i]=min(f[j]+max(h[j+1],…,h[i])) (s[i]−s[j]≤L)的话,可能处理转移区间的时候会有很多细节,不是特别容易实现。
代码
#include <bits/stdc++.h>
#define ll long long
#define N 100010
using namespace std;
template <typename T> void chkmax(T &x, T y) {x = x > y ? x : y;}
template <typename T> void chkmin(T &x, T y) {x = x > y ? y : x;}
template <typename T> void read(T &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {int l, r;} q[N];
int a[N], h[N]; ll sum[N], f[N];
int main() {
int n, L; read(n), read(L);
int l = 1, r = 0;
multiset <ll> s;
for (int i = 1; i <= n; i++) {
read(h[i]), read(a[i]);
sum[i] = sum[i - 1] + a[i]; int pos = i;
while (l <= r && h[q[r].r] <= h[i])
s.erase(s.find(f[q[r].l - 1] + h[q[r].r])), pos = q[r--].l;
q[++r] = (Node) {pos, i}; s.insert(f[q[r].l - 1] + h[i]);
while (l <= r && sum[i] - sum[q[l].l - 1] > L) {
s.erase(s.find(f[q[l].l - 1] + h[q[l].r])), q[l].l++;
if (q[l].l > q[l].r) l++; else s.insert(f[q[l].l - 1] + h[q[l].r]);
}
f[i] = *s.begin();
}
cout << f[n] << "\n";
return 0;
}