[题目链接]
https://www.lydsy.com/JudgeOnline/problem.php?id=2678
[算法]
首先不难想到如下DP :
记f[i]表示前i本书的高度和最小值
显然 , 有状态转移方程 : f[i] = min{ fj + max{hj+1 , hj+2 , ... hi} }
不难发现 , 当i确定时 , 随着j的减小 , max{hj + 1 , hj+2 , ... hi}的值单调递增
不妨维护一个单调递减的单调栈
预处理前缀和 , 每次在单调栈中二分出最靠左的左端点
然后 , 我们还需维护一棵支持单点修改 , 区间查询的线段树
每次在线段树中找到从合法左端点到当前点的最小值
详见代码 , 时间复杂度 : O(NlogN)
#include<bits/stdc++.h> using namespace std; #define MAXN 100010 typedef long long LL; const LL inf = 1e18; struct info { LL h , w; } a[MAXN]; LL n , top; LL L; int s[MAXN]; LL cnt[MAXN] , dp[MAXN]; struct Segment_Tree { struct Node { int l , r; LL mn; } Tree[MAXN << 2]; inline void build(int index , int l , int r) { Tree[index] = (Node){l , r , inf}; if (l == r) return; int mid = (l + r) >> 1; build(index << 1 , l , mid); build(index << 1 | 1 , mid + 1 , r); } inline void update(int index) { Tree[index].mn = min(Tree[index << 1].mn , Tree[index << 1 | 1].mn); } inline void modify(int index , int pos , LL value) { if (Tree[index].l == Tree[index].r) { Tree[index].mn = value; return; } int mid = (Tree[index].l + Tree[index].r) >> 1; if (mid >= pos) modify(index << 1 , pos , value); else modify(index << 1 | 1 , pos , value); update(index); } inline LL query(int index , int l , int r) { if (l > r) return inf; if (Tree[index].l == l && Tree[index].r == r) return Tree[index].mn; int mid = (Tree[index].l + Tree[index].r) >> 1; if (mid >= r) return query(index << 1 , l , r); else if (mid + 1 <= l) return query(index << 1 | 1 , l , r); else return min(query(index << 1 , l , mid) , query(index << 1 | 1 , mid + 1 , r)); } } SGT; template <typename T> inline void chkmax(T &x , T y) { x = max(x , y); } template <typename T> inline void chkmin(T &x , T y) { x = min(x , y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } int main() { read(n); read(L); for (int i = 1; i <= n; i++) { read(a[i].h); read(a[i].w); cnt[i] = cnt[i - 1] + a[i].w; } SGT.build(1 , 1 , n + 1); s[top = 1] = 0; dp[0] = s[0] = 0; SGT.modify(1 , 1 , 0); for (int i = 1; i <= n; i++) { while (top > 0 && a[i].h > a[s[top]].h) --top; s[++top] = i; SGT.modify(1 , top , dp[s[top - 1]] + a[i].h); int l = 1 , r = top , pos = 0; while (l <= r) { int mid = (l + r) >> 1; if (cnt[i] - cnt[s[mid]] <= L) { pos = mid; r = mid - 1; } else l = mid + 1; } int loc = lower_bound(cnt , cnt + n + 1 , cnt[i] - L) - cnt; dp[i] = SGT.query(1 , pos + 1 , top); chkmin(dp[i] , dp[loc] + a[s[pos]].h); } cout<< dp[n] << ' '; return 0; }