Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
For a fixed value , replace each by or . If you use for , then a subarray has a median at least exactly when its transformed sum is nonnegative.
That means the largest submedian can be found by binary search plus prefix sums. Symmetrically, using for finds the smallest submedian.
The set of all submedians has no holes. The reason is that every subarray contributes a median interval, and if two subarrays differ by adding/removing one endpoint, their median intervals intersect.
Once you have witnesses for the minimum and maximum submedians, walk from one witness subarray to the other by moving endpoints one step at a time while keeping length at least . The union of median intervals along this walk covers every value between the extremes.
Maintain the current subarray with a Fenwick tree over values , so you can get the two middle order statistics. Use a DSU-style next-unassigned array to fill answer witnesses over median intervals without looping over the same value again and again.
For one subarray, medians are not necessarily a single value. Sort its values as
An integer is a median iff at least elements are and at least elements are . So the median values are exactly
with integer division in the indices. For even length, this can include values not present in the array. That little detail is where a lot of wrong solutions go to die.
Finding The Extremes
First find the largest submedian.
For a fixed value , define
A subarray has some median at least iff at least half of its elements are , which is exactly
over that subarray.
So checking whether there exists a subarray of length at least with median at least becomes the classic prefix-sum check: for every right endpoint , keep the minimum prefix sum among positions at most . If
then such a subarray exists.
This predicate is monotonic in , so binary search gives the maximum submedian and a witness subarray for it.
The minimum submedian is symmetric. Define
Now binary search the smallest for which some length-at-least- subarray has nonnegative transformed sum. That gives and its witness.
The witnesses are valid for the exact extreme values: for the maximum, the found subarray has some median . If itself were not a median of it, then there would be a submedian greater than , contradiction. Same logic works for the minimum.
Why The Answer Is One Whole Range
Take two subarrays where one is obtained from the other by adding or deleting one endpoint. Their median intervals always intersect.
Why? If you insert one element into a sorted multiset:
Deleting is just the reverse operation. So adjacent subarrays share at least one median.
Now take the witness for , say , and the witness for , say . We build a path of valid subarrays:
Every subarray on this path has length at least . Consecutive subarrays have intersecting median intervals, so the union of all median intervals along the path is connected. It contains at the start and at the end, therefore it contains every integer from to .
Since and are the global minimum and maximum submedians, there is nothing outside this range. So the full answer is exactly every value in .
Maintaining The Current Median Interval
As we walk along the path, endpoints move one step at a time. We maintain frequencies of values in the current subarray using a Fenwick tree.
For current length , the median interval is
The Fenwick tree supports updates and order-statistic queries in .
When a current subarray contributes median interval , we need to assign this subarray as a witness for every still-unanswered value in that interval. Doing that naively can be quadratic. So use a DSU-next structure:
find(v) returns the smallest unanswered value at least ;find(v+1).Each value is erased once, so all interval-filling work is almost linear.
Correctness Proof
First, the binary search for is correct because the transformed sum condition with is equivalent to the existence of a subarray whose median interval reaches at least . The predicate is monotone, so binary search finds the largest possible such , which is exactly the largest submedian. The same argument with finds the smallest submedian .
Second, every subarray contributes a contiguous interval of medians, namely its two middle order statistics. Also, two subarrays differing by one endpoint have intersecting median intervals. Therefore, along any endpoint-by-endpoint path of valid subarrays, the union of median intervals is contiguous.
The constructed path starts at a subarray containing as a median and ends at a subarray containing as a median. Hence the union of median intervals processed by the algorithm contains all values from to .
Every value assigned by the algorithm is assigned from the actual median interval of the stored witness subarray, so every output triple is valid. The DSU-next structure only skips values that already have a valid witness, so every value is output at most once.
Thus the algorithm outputs exactly all submedians, each with a valid witness.
Complexity
Each binary search check is , and there are checks. The endpoint walk has moves, each doing Fenwick work in . DSU assignment is amortized near-linear.
Total complexity per test case:
Memory usage is .
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Fenwick {
int n = 0;
vector<int> bit;
Fenwick(int n = 0) { init(n); }
void init(int n_) {
n = n_;
bit.assign(n + 1, 0);
}
void add(int idx, int val) {
for (; idx <= n; idx += idx & -idx) bit[idx] += val;
}
int kth(int need) const {
int idx = 0;
int step = 1;
while ((step << 1) <= n) step <<= 1;
for (; step; step >>= 1) {
int nxt = idx + step;
if (nxt <= n && bit[nxt] < need) {
idx = nxt;
need -= bit[nxt];
}
}
return idx + 1;
}
};
struct SuccessorDSU {
int n = 0;
vector<int> parent;
SuccessorDSU(int n = 0) { init(n); }
void init(int n_) {
n = n_;
parent.resize(n + 2);
iota(parent.begin(), parent.end(), 0);
}
int find(int x) {
if (x > n) return n + 1;
int root = x;
while (root <= n && parent[root] != root) root = parent[root];
if (root > n) root = n + 1;
while (x <= n && parent[x] != x) {
int nxt = parent[x];
parent[x] = root;
x = nxt;
}
return root;
}
void erase(int x) {
parent[x] = find(x + 1);
}
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<int> a(n + 1), pref(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
auto check = [&](int v, bool wantSmall) -> pair<bool, array<int, 2>> {
pref[0] = 0;
for (int i = 1; i <= n; i++) {
bool good = wantSmall ? (a[i] <= v) : (a[i] >= v);
pref[i] = pref[i - 1] + (good ? 1 : -1);
}
int best = 0, bestIdx = 0;
for (int r = k; r <= n; r++) {
int cand = r - k;
if (pref[cand] < best) {
best = pref[cand];
bestIdx = cand;
}
if (pref[r] - best >= 0) {
return {true, {bestIdx + 1, r}};
}
}
return {false, {-1, -1}};
};
int lo = 1, hi = n;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (check(mid, true).first) hi = mid;
else lo = mid + 1;
}
int minVal = lo;
auto minSeg = check(minVal, true).second;
lo = 1, hi = n;
while (lo < hi) {
int mid = (lo + hi + 1) / 2;
if (check(mid, false).first) lo = mid;
else hi = mid - 1;
}
int maxVal = lo;
auto maxSeg = check(maxVal, false).second;
vector<int> ansL(n + 1, -1), ansR(n + 1, -1);
SuccessorDSU nxt(n);
Fenwick fw(n);
int L = minSeg[0], R = minSeg[1];
for (int i = L; i <= R; i++) fw.add(a[i], 1);
auto assignInterval = [&](int lVal, int rVal, int l, int r) {
int x = nxt.find(lVal);
while (x <= rVal) {
ansL[x] = l;
ansR[x] = r;
nxt.erase(x);
x = nxt.find(x);
}
};
auto record = [&]() {
int len = R - L + 1;
int lVal = fw.kth((len + 1) / 2);
int rVal = fw.kth((len + 2) / 2);
assignInterval(lVal, rVal, L, R);
};
record();
while (R < n) {
++R;
fw.add(a[R], 1);
record();
}
while (L < maxSeg[0]) {
fw.add(a[L], -1);
++L;
record();
}
while (L > maxSeg[0]) {
--L;
fw.add(a[L], 1);
record();
}
while (R > maxSeg[1]) {
fw.add(a[R], -1);
--R;
record();
}
vector<array<int, 3>> out;
for (int v = 1; v <= n; v++) {
if (ansL[v] != -1) out.push_back({v, ansL[v], ansR[v]});
}
cout << out.size() << '\n';
for (auto [v, l, r] : out) {
cout << v << ' ' << l << ' ' << r << '\n';
}
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Fenwick {
int n = 0;
vector<int> bit;
Fenwick(int n = 0) { init(n); }
void init(int n_) {
n = n_;
bit.assign(n + 1, 0);
}
void add(int idx, int val) {
for (; idx <= n; idx += idx & -idx) bit[idx] += val;
}
int kth(int need) const {
int idx = 0;
int step = 1;
while ((step << 1) <= n) step <<= 1;
for (; step; step >>= 1) {
int nxt = idx + step;
if (nxt <= n && bit[nxt] < need) {
idx = nxt;
need -= bit[nxt];
}
}
return idx + 1;
}
};
struct SuccessorDSU {
int n = 0;
vector<int> parent;
SuccessorDSU(int n = 0) { init(n); }
void init(int n_) {
n = n_;
parent.resize(n + 2);
iota(parent.begin(), parent.end(), 0);
}
int find(int x) {
if (x > n) return n + 1;
int root = x;
while (root <= n && parent[root] != root) root = parent[root];
if (root > n) root = n + 1;
while (x <= n && parent[x] != x) {
int nxt = parent[x];
parent[x] = root;
x = nxt;
}
return root;
}
void erase(int x) {
parent[x] = find(x + 1);
}
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<int> a(n + 1), pref(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
auto check = [&](int v, bool wantSmall) -> pair<bool, array<int, 2>> {
pref[0] = 0;
for (int i = 1; i <= n; i++) {
bool good = wantSmall ? (a[i] <= v) : (a[i] >= v);
pref[i] = pref[i - 1] + (good ? 1 : -1);
}
int best = 0, bestIdx = 0;
for (int r = k; r <= n; r++) {
int cand = r - k;
if (pref[cand] < best) {
best = pref[cand];
bestIdx = cand;
}
if (pref[r] - best >= 0) {
return {true, {bestIdx + 1, r}};
}
}
return {false, {-1, -1}};
};
int lo = 1, hi = n;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (check(mid, true).first) hi = mid;
else lo = mid + 1;
}
int minVal = lo;
auto minSeg = check(minVal, true).second;
lo = 1, hi = n;
while (lo < hi) {
int mid = (lo + hi + 1) / 2;
if (check(mid, false).first) lo = mid;
else hi = mid - 1;
}
int maxVal = lo;
auto maxSeg = check(maxVal, false).second;
vector<int> ansL(n + 1, -1), ansR(n + 1, -1);
SuccessorDSU nxt(n);
Fenwick fw(n);
int L = minSeg[0], R = minSeg[1];
for (int i = L; i <= R; i++) fw.add(a[i], 1);
auto assignInterval = [&](int lVal, int rVal, int l, int r) {
int x = nxt.find(lVal);
while (x <= rVal) {
ansL[x] = l;
ansR[x] = r;
nxt.erase(x);
x = nxt.find(x);
}
};
auto record = [&]() {
int len = R - L + 1;
int lVal = fw.kth((len + 1) / 2);
int rVal = fw.kth((len + 2) / 2);
assignInterval(lVal, rVal, L, R);
};
record();
while (R < n) {
++R;
fw.add(a[R], 1);
record();
}
while (L < maxSeg[0]) {
fw.add(a[L], -1);
++L;
record();
}
while (L > maxSeg[0]) {
--L;
fw.add(a[L], 1);
record();
}
while (R > maxSeg[1]) {
fw.add(a[R], -1);
--R;
record();
}
vector<array<int, 3>> out;
for (int v = 1; v <= n; v++) {
if (ansL[v] != -1) out.push_back({v, ansL[v], ansR[v]});
}
cout << out.size() << '\n';
for (auto [v, l, r] : out) {
cout << v << ' ' << l << ' ' << r << '\n';
}
}
return 0;
}