Appearance
Omkar and Medians -- CF 1536D
Difficulty: 1700 * Techniques: constructive, invariant checking, set with ordered queries Problem Link
Quick Revisit
- PROBLEM: Can a sequence be consecutive medians of a growing array?
- KEY INSIGHT: Don't construct -- just check. No previously-used median value may lie strictly between consecutive medians.
- TECHNIQUES: constructive (existence check), set with lower_bound, invariant reasoning
- TRANSFER: "Does a valid construction exist?" -- find the necessary condition that separates YES/NO, skip actual construction.
First Read
Given a sequence
Actually, re-reading: we have a sequence of
My First Wrong Approach
I initially tried to construct the actual array -- inserting two elements at each step to shift the median to
Then I realized: we don't need to construct anything. We just need to check if the sequence
What Makes a Sequence Invalid?
Think about consecutive medians
For every consecutive pair
Why: previously appearing medians are guaranteed to still be in the array (medians come from the array's elements and are never removed). If such a value
We only know with certainty about previously appearing medians, but that is enough -- they are the only values we are forced to include. So the check reduces to: between consecutive medians, no previously-used median value lies strictly between.
A small example of the violation
Suppose
- After step 1, the array is
with median 5. Used set: . - Step 1 to step 2: median moves from 5 to 2. Range
contains nothing in the used set, so this is allowed. Used set: . - Step 2 to step 3: median moves from 2 to 8. Range
contains 5 -- and 5 is in the used set. Output NO.
The intuition: 5 is sitting in the array, and the median can't leap from 2 over 5 to 8 by inserting only two elements.
Compare with lower_bound(lo + 1) then *it < hi idiom captures.
The Algorithm
- Maintain a set of "used" median values.
- For each consecutive pair
where : - Check if any value in the used set lies strictly between
and . - If yes: output NO.
- If no: add
to the used set (if not already there).
- Check if any value in the used set lies strictly between
- If we never fail, output YES.
The "check if any value lies strictly between" is a lower_bound / upper_bound query on a std::set. Clean and
Implementation Notes
- If
, no check needed -- the median doesn't move. - We add
to the set before processing the next transition, not after. Think of it as: has been "used" and is now in the array. - Edge case:
is always YES.
The Code
cpp
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n; cin >> n;
vector<int> b(n);
for (auto& x : b) cin >> x;
set<int> used;
used.insert(b[0]);
for (int i = 0; i + 1 < n; i++) {
if (b[i] == b[i + 1]) {
continue;
}
int lo = min(b[i], b[i + 1]);
int hi = max(b[i], b[i + 1]);
auto it = used.lower_bound(lo + 1);
if (it != used.end() && *it < hi) {
cout << "NO\n";
return;
}
used.insert(b[i + 1]);
}
cout << "YES\n";
}
int main() {
int t; cin >> t;
while (t--) solve();
}Transfer Lesson
- "Does a valid construction exist?" -- often you don't need to construct, just check necessary conditions. Find the invariant that separates YES from NO.
- Strictly-between check --
lower_bound(lo + 1)then compare withhiis the idiom for checking if a set has any element in the open interval. - Median problems -- the median is "sticky." Values near it resist change, so consecutive medians can't jump over previously-established values.
See also: Constructive Algorithms | Set and Multiset | Invariant Technique | Pattern Recognition Guide | Practice Ladder 1400-1700