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

冷却心
111搬运于
2025-08-24 23:08:32,当前版本为作者最后更新于2025-06-03 09:29:39,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
参考了官方题解。方便起见,下文不区分 。
对于一个石油连通块,我们记其最左侧在第 列,最右侧在第 列,那么在 中任选一列就会造成贡献,所以我们把每个连通块缩成一个线段 附带石油总和作为权值,问题变为:
给定 条线段 附加 的权值,对每个 求出在 到 中选择 个整点,使得和这些整点有交的线段的权值总和最大,求出最大权值和。, 是平方级别,但是保证线段长度之和至多 。
考虑 dp。记状态 表示在前 列中选了 个,其中第 个必选,最大代价。转移是显然的:
其中 表示不包含 但是包含 的线段权值和。 不是很好求,我们先求 表示以 为左端点并且包含 的线段权值和。这个是好求的,遍历每条线段即可。然后有转移:
表示不包含 但是包含 的线段权值和,由不包含 但是包含 的线段以及以 为左端点且包含 的线段组成。
然后做完了,这样 求 , dp。
然后考虑优化。先给出结论,dp 状态具有决策单调性,即记 表示 的最优决策点,那么保证 。于是我们按 分层转移,每次转移用分治维护就好了。时间复杂度 。
决策单调性的不严谨证明:
首先当 不存在,也就是 的时候结论显然成立,不再讨论。
为了便于书写记 ,考虑反证法,设 ,那么我们有:
$$\begin{aligned} f_{i,j}=f_{p,j-1}+c(p,i)&>f_{q,j-1}+c(q,i),\\ f_{i+1,j}=f_{q,j-1}+c(q,i+1)&\ge f_{p,j-1}+c(p,i+1),\\ \end{aligned} $$上下两式相加化简得到:
我们记 表示以 结尾且不包含 的线段权值和,那么我们可以得到一个类似的关于 的递推式:
那么:
$$\begin{aligned} c(p,i+1)&=c(p,i)-g(i,p),\\ c(q,i+1)&=c(q,i)-g(i,q). \end{aligned} $$代入不等式化简得到 。但是 固定 ,当 越小的时候显然 越大,而 却满足 ,所以矛盾,决策单调性得证。
#include <bits/stdc++.h> #define LL long long using namespace std; const int N = 2e3 + 10; int n, m; char S[N][N]; bool vis[N][N]; int cur, mn, mx; int dx[4] = {0, 1, 0, -1}; int dy[4] = {1, 0, -1, 0}; void DFS(int x, int y) { mn = min(mn, y); mx = max(mx, y); cur += (S[x][y] - '0'); vis[x][y] = 1; for (int i = 0; i < 4; i ++) { int a = x + dx[i], b = y + dy[i]; if (a >= 1 && a <= n && b >= 1 && b <= m && !vis[a][b] && S[a][b] != '.') DFS(a, b); } return ; } struct Seg { int l, r, k; } seg[N * N]; int tot; vector<int> vec[N]; int W[N][N], cost[N][N], DP[N][N]; void Solve(int l, int r, int pl, int pr, int id) { if (l > r || pl > pr) return ; int mid = (l + r) >> 1, k = pl; for (int i = pl + 1; i <= min(pr, mid - 1); i ++) if (DP[i][id - 1] + cost[i][mid] > DP[k][id - 1] + cost[k][mid]) k = i; DP[mid][id] = DP[k][id - 1] + cost[k][mid]; Solve(l, mid - 1, pl, k, id); Solve(mid + 1, r, k, pr, id); } int main() { freopen(".in", "r", stdin); freopen(".out", "w", stdout); ios :: sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m; for (int i = 1; i <= n; i ++) cin >> (S[i] + 1); for (int i = 1; i <= n; i ++) for (int j = 1; j <= m; j ++) if (S[i][j] != '.' && !vis[i][j]) { mn = m + 1, cur = mx = 0; DFS(i, j); seg[++ tot] = {mn, mx, cur}; vec[mn].push_back(tot); } for (int i = 1; i <= m; i ++) for (int x : vec[i]) for (int j = seg[x].l; j <= seg[x].r; j ++) W[i][j] += seg[x].k, DP[j][1] += seg[x].k; for (int i = m; i >= 1; i --) for (int j = i + 1; j <= m; j ++) cost[i][j] = cost[i + 1][j] + W[i + 1][j]; for (int i = 2; i <= m; i ++) Solve(i, m, 1, m, i); for (int i = 1; i <= m; i ++) { int res = 0; for (int j = 1; j <= m; j ++) res = max(res, DP[j][i]); cout << res << "\n"; } return 0; }
- 1
信息
- ID
- 11176
- 时间
- 2000ms
- 内存
- 500MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者