Med-imize§
显然题目满足二分性质。
而如何在
check中判断某个值是否满足呢?很自然地想到,可以用动态规划来解决。
在这之前我们需要对给定的数组进行预处理,将原数组大于当前二分的答案的值设置为
1, 否则为-1。我们会用动态规划来算出题目操作后留下来位置值的和最大是多少。
当这个值大于
0则该答案合法。但如何进行动态规划呢?
这里我们需要了解一些有关题目操作的性质。
对数组各个位置的下标(从
0开始)对k取模,肯定能得到类似于:
0 1 2 ... k - 1 0 1 2 ... k - 1 0 1 2 ... k - 1这里我们用
0 1 2 3 0 1 2 3 0 1 2 3代替表示,此时k等于4。可以发现一个性质,任意位置对该数组进行操作,假如:
0 1 2 [3 0 1 2] 3 0 1 2 3,得到的新数组为:0 1 2 3 0 1 2 3。那么最后得到的数组肯定为:
0 1 2 3(这里原数组长度是k的倍数,若不是则最大值为末尾下标对k取模)。说到这,其实思路就很明确了。
我们其实求的是按顺序选择处理过的下标为
0 1 2 3的值的和的最大值。0 1 2 3 0 1 2 3 0 1 2 3显然二维
DP求解一下就可以了,这里不多讲解。接下来解释怎么压缩到一维(复杂度和上面都一样但这个会简洁些)。
设 \(dp_i\) 为选到以 \(i \mod k\) 为结尾时的最大值。
\(dp_i = \begin{cases} a_i ,i \mod k = 0 \\ dp_{i-1} +a_i ,otherwise\end{cases},a_i = (a[i] >= u) ? 1 : -1\)
\(dp_i = \max(dp_i, dp_{i-k} ),i \ge k\)
完事。
#include <bits/stdc++.h> void solve() { int n, k; std::cin >> n >> k; std::vector<int> a(n); for (auto &it : a) { std::cin >> it; } auto check = [&](int u) -> bool { std::vector<int> dp(n); for (int i = 0; i < n; i++) { int x = (a[i] >= u) ? 1 : -1; if (i % k == 0) { dp[i] = x; } else { dp[i] = dp[i - 1] + x; } if (i >= k) { dp[i] = std::max(dp[i], dp[i - k]); } } return dp[n - 1] > 0; }; int l = 1, r = 1e9; while (l < r) { int mid = (l + r + 1) / 2; if (check(mid)) { l = mid; } else { r = mid - 1; } } std::cout << l << '\n'; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int t; std::cin >> t; while (t--) { solve(); } return 0; }