1417 字
7 分钟
CF1085 (Div. 1 + Div. 2) C 题解
C. Where’s My Water?
题意
给定一个长度为 , 高度为 的水槽, 位置存在淤泥,深度为
你可以安置排水管,每个排水管可以排出所有可以通过 向 下,左,右 移动不经过淤泥到排水管的水

问安置至多两个排水管最多有多少水被排出
It is guaranteed that the sum of over all test cases does not exceed .
题解
显然有 实现在位置 安置一个排水管, / 位置排出的水的数量( 较小,为了方便采用 的做法)
那么只需要枚举 位置放排水管,计算 排出的水
考虑均摊 解决,维护一个单峰的序列,对于新加入的位置 ,将序列中所有 的全部弹出,直到到达峰顶
计算贡献,每个 的贡献为 ,另外再维护峰顶的贡献即可
但是我场上错误的认为 贡献是 ,之前想到了 但是否掉了,结束也没看出来 提交记录
77 collapsed lines
#include <bits/stdc++.h>using namespace std;using ll = long long;const int N = 2000 + 5;int n, h;int a[N];ll pre[N][2];void solve() { ll ans = 0; cin >> n >> h; for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 1; i <= n; i++) { int l = i, r = i;
int t = a[l]; ll res = 0; for(int j = l - 1; j >= 1; j--) t = max(t, a[j]), res += h - t; pre[i][0] = res;
t = a[r]; res = 0; for(int j = r + 1; j <= n; j++) t = max(t, a[j]), res += h - t; pre[i][1] = res; }
for(int i = 1; i <= n; i++) { ans = max(ans, pre[i][0] + pre[i][1] + h - a[i]); }
deque<int> q; auto calc = [](int x, int y) -> ll { if(a[x] < a[y]) return 1ll * (y - x) * (h - a[x]); return 1ll * (y - x) * (h - a[y]); };
for(int i = 1; i + 1 <= n; i++) { while(!q.empty()) q.pop_back();
ll res = h - a[i]; q.push_back(i); int fin = i;
for(int j = i + 1; j <= n; j++) {
while(!q.empty() && a[q.back()] < a[j]) { if(q.back() == fin) break; int back0 = q.back(); q.pop_back(); int back1 = q.back(); res -= calc(back1, back0); }
int back0 = j; int back1 = q.back(); if(back1 == fin && a[back0] >= a[back1]) { res -= h - a[fin]; fin = back0; res += h - a[fin]; } res += calc(back1, back0);
q.push_back(back0); ll cur = pre[i][0] + pre[j][1] + res; ans = max(ans, cur); } } cout << ans << endl;}signed main() { ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); // freopen("A.in", "r", stdin); // freopen("A.out", "w", stdout); int T = 1; cin >> T; while(T--) solve();}解法1
但是有更加简单的做法:
与之前解法相同, 可以得到 在 位置放一个排水管,得到 / 排出的水
那么枚举中间点 ,可以 枚举 水管位置 和 右端点 维护 排出水量的最大值,同理得到 的最大值
就是答案
54 collapsed lines
#include <bits/stdc++.h>using namespace std;using ll = long long;const int N = 2000 + 5;int n, h;int a[N];ll f[N][2];void solve() { cin >> n >> h; for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 1; i <= n; i++) f[i][0] = f[i][1] = 0; for(int i = 1; i <= n; i++) { int t = a[i]; ll res = h - t; for(int j = i - 1; j >= 1; j--) t = max(t, a[j]), res += h - t; f[i][0] = max(f[i][0], res);
t = a[i]; for(int j = i + 1; j <= n; j++) { t = max(t, a[j]), res += h - t; f[j][0] = max(f[j][0], res); } }
for(int i = 1; i <= n; i++) { int t = a[i]; ll res = h - t; for(int j = i + 1; j <= n; j++) t = max(t, a[j]), res += h - t; f[i][1] = max(f[i][1], res);
t = a[i]; for(int j = i - 1; j >= 1; j--) { t = max(t, a[j]), res += h - t; f[j][1] = max(f[j][1], res); } }
// for(int i = 1; i <= n; i++) cout << f[i][0] << " \n"[i == n]; // for(int i = 1; i <= n; i++) cout << f[i][1] << " \n"[i == n];
ll ans = max(f[n][0], f[1][1]); for(int i = 1; i + 1 <= n; i++) ans = max(ans, f[i][0] + f[i + 1][1]); cout << ans << endl;
}int main() { ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); // freopen("A.in", "r", stdin); // freopen("A.out", "w", stdout); int T = 1; cin >> T; while(T--) solve();}解法2
但是有更加简单的做法:
依然考虑枚举 排水管的两个位置 ,notice that 重复的部分 恰好是最高位置 的 贡献 ,不过显然不是我 notice 到的()
对于位置 的水,它被统计两次时有:
- 对于 ,等价于
- 对于 或 ,等价于
令 ,发现情况 均等价于 排水的定义
故答案为 排水 + 排水 - 排水
每次 维护 max_k 即可
41 collapsed lines
#include <bits/stdc++.h>using namespace std;using ll = long long;const int N = 2000 + 5;int n, h;int a[N];ll f[N];void solve() { cin >> n >> h; for(int i = 1; i <= n; i++) cin >> a[i]; for(int i = 1; i <= n; i++) { int t = a[i]; ll res = h - t; for(int j = i - 1; j >= 1; j--) t = max(t, a[j]), res += h - t;
t = a[i]; for(int j = i + 1; j <= n; j++) t = max(t, a[j]), res += h - t; f[i] = res; }
ll ans = 0; for(int i = 1; i <= n; i++) ans = max(ans, f[i]);
for(int i = 1; i <= n; i++) { int maxi = i; for(int j = i; j <= n; j++) { if(a[maxi] < a[j]) maxi = j; ans = max(ans, f[i] + f[j] - f[maxi]); } } cout << ans << endl;}int main() { ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); // freopen("A.in", "r", stdin); // freopen("A.out", "w", stdout); int T = 1; cin >> T; while(T--) solve();} CF1085 (Div. 1 + Div. 2) C 题解
https://dk-qwq.github.io/blog/posts/cf2207/cf2207c/