题解:P17143 [NOI 2026] 中位数(暂无数据)
为啥大家做法都这么复杂,吓哭了,这里提供一个不需要太多分类讨论的做法。
首先显然二分答案
先考虑简单的
考虑如何判断能否凑出相邻的中位数
而对于两个距离 1001 可以凑成 10|01。这就说明我们凑大的合法段是完全没有必要的。
所以
接下来考虑
-
当靠前的相邻合法段为
1001时,后面若再接001,这7 个数是不能抵消掉两段的。 -
若靠前的相邻合法段为
101或11,则后面接001是可以再消掉一段的。 -
不管靠前的相邻合法段或者开头段是什么,后面接
01或1都是可以再消掉一段的。
其实可以一句话概括的,上面的细节都归结于:11 可以划分为 1|1,101 可以划分为 10|1,1001 可以划分为 10|01。
最后的话,其他题解也都提到了,注意特判
#include "median.h"
#include <bits/stdc++.h>
using namespace std;
void init(int c, int t) {
return;
}
int n, k, a[1000005], b[1000005], one[1000005];
int f[1000005][6][6], hou[1000005];
int now = 0;
int ch(int x) {
for (int i = 1; i <= n; i++)
b[i] = (a[i] >= x), hou[i] = 0;
int m = 0;
for (int i = 1; i <= n; i++)
if (b[i])
one[++m] = i;
int xu = (k + 1) / 2;
if (m < xu)
return 0;
if (k <= 5) {
int d = k / 2, u = (k + 1) / 2;
for (int i = 0; i <= n; i++)
for (int j = 0; j <= d; j++)
for (int k = 0; k <= u; k++)
f[i][j][k] = -1e9;
f[1][0][0] = 1;
if (!b[1])
f[1][0][0] = -1;
for (int i = 2; i <= n; i++) {
for (int j = 0; j <= d; j++) {
for (int k = 0; k <= u; k++) {
int z = b[i];
if (!b[i])
z = -1;
f[i][j][k] = max(f[i][j][k], f[i - 1][j][k] + z);
if ((j - 1 >= 0 && f[i - 1][j - 1][k] + n >= 0) || (k - 1 >= 0 && f[i - 1][j][k - 1] >= 0))
f[i][j][k] = max(f[i][j][k], z);
}
}
}
return f[n][d - 1][u] + n >= 0 || f[n][d][u - 1] >= 0;
}
if (k % 2 == 0) {
int f = 0;
if (one[1] <= 2 || one[m] >= n - 1)
f = 1;
for (int i = 1; i < m; i++)
if (one[i + 1] - one[i] <= 3)
f = 1;
return f;
}
for (int i = 1; i <= m; i++)
if (one[i] <= 2 || one[i] >= n - 1 || (i + 1 <= m && one[i + 1] - one[i] <= 3))
hou[i] = 1;
for (int i = m - 1; i >= 1; i--)
hou[i] = max(hou[i], hou[i + 1]);
for (int i = 1; i <= m; i++) {
if (one[i] <= 2 || (i - 1 >= 1 && one[i] - one[i - 1] <= 3))
if (i + 1 <= m && (one[i + 1] - one[i] <= 2 || hou[i + 1]))
return 1;
if (one[i] <= 1 || (i - 1 >= 1 && one[i] - one[i - 1] <= 2))
if (i + 1 <= m && (one[i + 1] - one[i] <= 3 || hou[i + 1]))
return 1;
if (i - 1 >= 1 && ((one[i] - one[i - 1] <= 2 && one[i] >= n - 1) || (one[i] - one[i - 1] == 3 && one[i] == n)))
return 1;
}
return 0;
}
int median(int n_, int k_, std::vector<int> a_) {
n = n_;
k = k_;
for (int i = 1; i <= n; i++)
a[i] = a_[i - 1];
int l = 1, r = n, ans = 0;
while (l <= r) {
int mid = (l + r) / 2;
if (ch(mid))
ans = mid, l = mid + 1;
else
r = mid - 1;
}
return ans;
}