1 条题解
-
0
自动搬运
来自洛谷,原作者为

EuphoricStar
Remember.搬运于
2025-08-24 23:13:37,当前版本为作者最后更新于2025-04-14 22:26:27,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
考虑将最终的局面划分成,若干个极长的,每一列都至少有一个滑动墙的列连续段。设划分出来的连续段是 ,那么不用被解锁的滑动墙的花费是 ,其中 表示所有满足 的滑动墙的 之和。一种方案合法,当且仅当:
- $\sum\limits_{i = 1}^h c_i - \sum\limits_{i = 1}^t g(l_i, r_i) \le k$,即花费不能超过 ;
- 令 ,则 ,因为要给最长的滑动墙留够位置放。
于是我们转而求满足条件的划分方案中, 的最大值。接下来的 DP 是自然的。设 表示,考虑到 这个前缀,其中有 列没有滑动墙,当前是否出现长度 的连续段。有两种转移:
- 第 位留空,有 ;
- 第 位是一个连续段的右端点,枚举这个连续段的左端点 ,有 $f_{i, j, k \lor [i - l + 1 \ge x]} \gets f_{l - 1, j, k} + g(l, i)$。
朴素转移时间复杂度为 ,无法通过。考虑优化。发现瓶颈在第二种转移,考虑在最外层枚举 ,那么转移形如 。考虑线段树优化。线段树每个下标 维护 ,枚举到 时加入所有 的滑动墙,即对 区间加上 。计算 时求线段树区间最大值即可。
设 同阶,时间复杂度 。
#include <bits/stdc++.h> #define fst first #define scd second #define pb emplace_back #define mkp make_pair #define mems(a, x) memset((a), (x), sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; typedef double db; typedef long double ldb; typedef pair<int, int> pii; const int maxn = 2020; ll n, m, K, f[maxn][maxn][2]; vector<pii> vc[maxn]; struct node { ll l, r, x; } a[maxn]; struct SGT { ll a[maxn << 2], tag[maxn << 2]; inline void pushup(int x) { a[x] = max(a[x << 1], a[x << 1 | 1]); } inline void pushtag(int x, ll y) { a[x] += y; tag[x] += y; } inline void pushdown(int x) { if (!tag[x]) { return; } pushtag(x << 1, tag[x]); pushtag(x << 1 | 1, tag[x]); tag[x] = 0; } void build(int rt, int l, int r) { tag[rt] = 0; a[rt] = -1e18; if (l == r) { return; } int mid = (l + r) >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); } void update(int rt, int l, int r, int ql, int qr, ll x) { if (ql <= l && r <= qr) { pushtag(rt, x); return; } pushdown(rt); int mid = (l + r) >> 1; if (ql <= mid) { update(rt << 1, l, mid, ql, qr, x); } if (qr > mid) { update(rt << 1 | 1, mid + 1, r, ql, qr, x); } pushup(rt); } void modify(int rt, int l, int r, int x, ll y) { if (l == r) { a[rt] = y; return; } pushdown(rt); int mid = (l + r) >> 1; (x <= mid) ? modify(rt << 1, l, mid, x, y) : modify(rt << 1 | 1, mid + 1, r, x, y); pushup(rt); } ll query(int rt, int l, int r, int ql, int qr) { if (ql > qr) { return -1e18; } if (ql <= l && r <= qr) { return a[rt]; } pushdown(rt); int mid = (l + r) >> 1; ll res = -1e18; if (ql <= mid) { res = max(res, query(rt << 1, l, mid, ql, qr)); } if (qr > mid) { res = max(res, query(rt << 1 | 1, mid + 1, r, ql, qr)); } return res; } } T[2]; void solve() { scanf("%lld%lld%lld", &n, &m, &K); for (int i = 1; i <= n; ++i) { scanf("%lld%lld%lld", &a[i].l, &a[i].r, &a[i].x); vc[a[i].r].pb(a[i].l, a[i].x); } ll s = 0, mx = 0; for (int i = 1; i <= n; ++i) { s += a[i].x; mx = max(mx, a[i].r - a[i].l + 1); } mems(f, -0x3f); f[0][0][0] = 0; for (int j = 0; j <= m; ++j) { T[0].build(1, 1, m); T[1].build(1, 1, m); for (int i = 1; i <= m; ++i) { for (int k = 0; k <= 1; ++k) { if (j) { f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][k]); } } T[0].modify(1, 1, m, i, f[i - 1][j][0]); T[1].modify(1, 1, m, i, f[i - 1][j][1]); for (pii p : vc[i]) { T[0].update(1, 1, m, 1, p.fst, p.scd); T[1].update(1, 1, m, 1, p.fst, p.scd); } f[i][j][0] = max(f[i][j][0], T[0].a[1]); f[i][j][1] = max({f[i][j][1], T[1].a[1], T[0].query(1, 1, m, 1, i - mx + 1)}); } } for (int i = m; ~i; --i) { if (s - f[m][i][1] <= K) { printf("%d\n", i); return; } } } int main() { int T = 1; // scanf("%d", &T); while (T--) { solve(); } return 0; }
- 1
信息
- ID
- 11896
- 时间
- 4000ms
- 内存
- 1024MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者