BD202602 最小极差 题解
BD202602 最小极差
Solution
核心观察
记 \(\text{func }\text{check}(l,r) \to \{\text{True},\text{False}\}\) 表示能否通过至多 \(m\) 次翻转,得到一个长度 \(\ge k\) 的、值域在 \([l,r]\) 内的连续段。于是
所有极差取到 \(\text{ans}\) 的值域 \([l,r]\) 均满足 \(\text{check}(l,r)\),于是 \(\text{ans} \in \{r-l: \text{check}(l,r)\}\)。
假设存在元素 \(r_0 - l_0\) 满足 \(\text{check}(l_0,r_0)\) 且 \(r_0 - l_0 < \text{ans}\),记 \(m\) 次翻转后值域在 \([l_0,r_0]\) 内的连续段为 \(C\),\(C\) 的任一长度为 \(k\) 的连续子段为 \(C'\),\(C'\) 的值域为 \([l',r']\),那么 \(l_0 \le l' \le r' \le r_0\),于是存在一个长度为 \(k\) 的区间的极差 \(r'-l'\) 满足 \(r'-l' \le r_0 - l_0 < \text{ans}\)。这与 \(\text{ans}\) 定义矛盾,故 \(\text{ans} = \min \{r-l: \text{check}(l,r)\}\)。
只要能够枚举所有值域并检查 \(\text{check}\) 函数,问题就能解决。直接枚举的复杂度为 \(O(n^2)\),考虑双指针优化。
双指针优化
首先对值域离散化,记离散化后值域为 \([1, \mathit{mx}]\)。\(\text{check}\) 函数具有关键性质:若 \([l_0,r_0] \subset [l,r]\),则 \(\text{check}(l_0,r_0) \to \text{check}(l,r)\)。
初始化两个指针 \(l=1,r=1\),重复执行直到 \(r > \mathit{mx}\):
- 如果 \(\text{check}(l,r)\) 为 \(\text{True}\),那么更新答案,自增 \(l\);
- 如果 \(\text{check}(l,r)\) 为 \(\text{False}\),那么自增 \(r\);
考虑当前 \(r\) 所有检查过的值域 \([l,r]\),\(l\) 从小到大依次设为 \(l_1,l_2,\dots,l_z\),所有 \(l\) 相邻差为 \(1\),只有 \(\text{check}(l_z, r)\) 为 \(\text{False}\),其余均为 \(\text{True}\)。
对所有 \(l > l_z\),由关键性质的逆否命题可知,\(\text{check}(l,r)\) 均是 \(\text{False}\),不用检查。
对所有 \(l<l_1\),由关键性质可知,\(\text{check}(l,r)\) 均是 \(\text{True}\),而 \(r-l>r-l_1\),无需更新答案。
check 函数的转化
\(\text{check}(l,r)\) 为 \(\text{True}\) 的充分必要条件是前 \(m+1\) 个值域在 \([l,r]\) 内的极大连续段长度之和 \(\ge k\)。
区间维护
为了快速检查 \(\text{check}\) 函数,考虑动态维护值域在当前 \([l,r]\) 内的连续段。用 std::set 或 std::map 来存连续段区间(类似 ODT);使用 std::multiset 来模拟对顶堆,用于快速求出前 \(m+1\) 个值域在 \([l,r]\) 内的极大连续段长度之和。
Code
#include <bits/stdc++.h>
#define vi vector<int>
#define inc(i,a,b) for(auto i=(a);i<=(b);++i)
#define lbd lower_bound
#define ubd upper_bound
using namespace std;
template <typename T>
inline bool chmin(T& a, const T& b) {
if (b < a) { a = b; return true; }
return false;
}
struct Solver {
int n, m, k, sum;
vi a, b;
map<int, vi> c;
map<int, int> s;
multiset<int> p, q;
Solver() : sum(0) {
cin >> n >> m >> k;
a.resize(n + 1);
inc(i, 1, n) cin >> a[i];
b = a;
sort(b.begin() + 1, b.end());
b.erase(unique(b.begin() + 1, b.end()), b.end());
inc(i, 1, n) a[i] = lbd(b.begin() + 1, b.end(), a[i]) - b.begin();
int mx = b.size() - 1;
inc(i, 1, n) c[a[i]].emplace_back(i);
int l = 1, r = 0, res = 1 << 30;
while (r <= n) {
if (sum >= k) chmin(res, b[r] - b[l]);
if (sum >= k && l == r) { cout << 0 << '\n'; return; }
else if (sum >= k) { for (int i : c[l]) s_del(i); ++l; }
else { ++r; for (int i : c[r]) s_add(i); }
}
cout << res << '\n';
}
void s_add(int i) {
auto it = s.lbd(i);
// assert(it == s.end() || it->first > i);
if (it == s.begin()) {
int len = it->second - it->first + 1;
if (it->first == i + 1) {
h_del(len); h_add(len + 1);
int tmp = it->second;
s.erase(it);
s[i] = tmp;
}
else { h_add(1); s[i] = i; }
}
else if (it == s.end()) {
--it;
int len = it->second - it->first + 1;
if (it->second == i - 1) {
h_del(len); h_add(len + 1);
it->second = i;
}
else { h_add(1); s[i] = i; }
}
else {
auto r = it; auto l = --it;
int lenl = l->second - l->first + 1;
int lenr = r->second - r->first + 1;
if (l->second == i - 1 && r->first == i + 1) {
h_del(lenl); h_del(lenr); h_add(lenl + lenr + 1);
l->second = r->second;
s.erase(r);
}
else if (l->second == i - 1 && r->first != i + 1) {
h_del(lenl); h_add(lenl + 1);
l->second = i;
}
else if (l->second != i - 1 && r->first == i + 1) {
h_del(lenr); h_add(lenr + 1);
int tmp = r->second;
s.erase(r);
s[i] = tmp;
}
else { h_add(1); s[i] = i; }
}
}
void s_del(int i) {
auto it = s.ubd(i);
// assert(it != s.begin());
--it;
int len = it->second - it->first + 1;
if (len == 1) {
h_del(1); s.erase(it);
}
else if (it->first == i) {
h_del(len); h_add(len - 1);
int tmp = it->second;
s.erase(it);
s[i + 1] = tmp;
}
else if (it->second == i) {
h_del(len); h_add(len - 1);
it->second = i - 1;
}
else {
h_del(len); h_add(i - it->first); h_add(it->second - i);
s[i + 1] = it->second;
it->second = i - 1;
}
}
void h_add(int len) {
sum += len; p.insert(len);
if (p.size() > m + 1) {
q.insert(*p.begin());
sum -= *p.begin(); p.erase(p.begin());
}
}
void h_del(int len) {
auto it = p.find(len);
if (it != p.end()) {
sum -= len; p.erase(it);
if (p.size() < m + 1 && !q.empty()) {
it = q.end(); --it;
sum += *it; p.insert(*it);
q.erase(it);
}
}
else { q.erase(q.find(len)); }
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t; cin >> t;
while (t--) Solver s;
return 0;
}

浙公网安备 33010602011771号