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.
A segment can have at most two answers. Three different values each occurring more than would already need more than positions. So the whole problem is really: find a tiny candidate set, then verify it.
Do not try to count every value in every query. Store, for each distinct value, the sorted list of its positions. Then the frequency of value inside is just two binary searches.
Now you only need candidate generation. Use the generalized Boyer-Moore / Misra-Gries idea for threshold : keep two candidates with counters, and when a third different value appears, cancel one occurrence of all three.
Make that range-queryable with a segment tree. Each node stores the two residual candidates and their residual counters for its interval. To merge two nodes, feed the weighted candidates of one summary into the other summary using the same two-counter cancellation rule.
For a query, merge segment-tree nodes to get at most two possible answers. Then use the position lists to count them exactly in , keep only those with count , sort, and print. The summary only proposes suspects; binary search is the judge.
Key Observation
For a segment of length , there can be at most two values occurring strictly more than times.
If there were three different such values, their total occurrences would exceed . So every answer line has size at most two.
That means we do not need to find all frequent-ish values. We only need a tiny list of candidates, then verify them exactly.
Counting A Candidate
Before answering queries, store all positions of every compressed value:
Then the number of occurrences of in is:
So if someone hands us a candidate value, checking it is cheap: .
Now the actual problem is candidate generation.
Misra-Gries For One Segment
For threshold , the generalized Boyer-Moore algorithm keeps two candidates and two counters.
When reading a value :
That last step is the whole trick. It cancels a triple of three different values. A value that appears more than one third of the segment cannot be fully killed by these cancellations. So every real answer must survive as one of the final two candidates.
Important: the counters are not the real frequencies. They are just residual votes after cancellation. Treating them as exact counts is how bugs sneak in wearing fake glasses.
Making It Work For Range Queries
Build a segment tree. Each node stores a Misra-Gries summary of its interval:
A leaf contains exactly one value with counter .
To merge two children, start from the left summary and feed in the right summary's candidates with their residual weights. This is equivalent to saying: the right child has already canceled all irrelevant triples internally, and only its residual votes still matter for producing possible heavy hitters.
For threshold , feeding a weighted candidate can be done directly:
You can also insert one-by-one, but counters can be large, so the weighted version is cleaner.
For a query , combine the segment-tree nodes covering the range. The result gives at most two suspects. Then count those suspects exactly using the position lists.
Why The Segment Tree Summary Is Enough
Think of Misra-Gries as repeatedly deleting triples of distinct values. Deleting such triples cannot remove a value that is truly more than one third of the whole multiset from the candidate set forever, because every deleted triple removes at most one copy of that value while removing three total elements.
Each segment-tree node stores what remains after some valid triple deletions inside its interval. When two summaries are merged, we continue the same cancellation process on the remaining votes. So for the queried segment, the final two stored values are a superset of all values with frequency .
Then verification fixes everything:
No probability needed. No random sampling roulette. Deterministic AC, which is nicer because Codeforces RNG hacks are where dreams go to get mugged.
Complexity
Let and be the total input sizes over all test cases.
Total complexity:
with memory.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Node {
int v1 = -1, v2 = -1;
int c1 = 0, c2 = 0;
};
void addWeighted(Node &s, int x, int c) {
if (c <= 0) return;
if (s.c1 > 0 && s.v1 == x) {
s.c1 += c;
return;
}
if (s.c2 > 0 && s.v2 == x) {
s.c2 += c;
return;
}
if (s.c1 == 0) {
s.v1 = x;
s.c1 = c;
return;
}
if (s.c2 == 0) {
s.v2 = x;
s.c2 = c;
return;
}
int mn = min({s.c1, s.c2, c});
s.c1 -= mn;
s.c2 -= mn;
c -= mn;
if (s.c1 == 0) s.v1 = -1;
if (s.c2 == 0) s.v2 = -1;
if (c > 0) addWeighted(s, x, c);
}
Node mergeNode(Node a, const Node &b) {
if (b.c1 > 0) addWeighted(a, b.v1, b.c1);
if (b.c2 > 0) addWeighted(a, b.v2, b.c2);
return a;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
vector<ll> a(n), vals;
vals.reserve(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
vals.push_back(a[i]);
}
sort(vals.begin(), vals.end());
vals.erase(unique(vals.begin(), vals.end()), vals.end());
int m = (int)vals.size();
vector<int> comp(n);
vector<vector<int>> pos(m);
for (int i = 0; i < n; i++) {
comp[i] = lower_bound(vals.begin(), vals.end(), a[i]) - vals.begin();
pos[comp[i]].push_back(i + 1);
}
int sz = 1;
while (sz < n) sz <<= 1;
vector<Node> seg(2 * sz);
for (int i = 0; i < n; i++) {
seg[sz + i].v1 = comp[i];
seg[sz + i].c1 = 1;
}
for (int i = sz - 1; i >= 1; i--) {
seg[i] = mergeNode(seg[i << 1], seg[i << 1 | 1]);
}
auto rangeQuery = [&](int l, int r) {
Node left, right;
l += sz - 1;
r += sz - 1;
while (l <= r) {
if (l & 1) left = mergeNode(left, seg[l++]);
if (!(r & 1)) right = mergeNode(seg[r--], right);
l >>= 1;
r >>= 1;
}
return mergeNode(left, right);
};
auto countInRange = [&](int id, int l, int r) {
const vector<int> &p = pos[id];
return (int)(upper_bound(p.begin(), p.end(), r) - lower_bound(p.begin(), p.end(), l));
};
while (q--) {
int l, r;
cin >> l >> r;
int len = r - l + 1;
int need = len / 3 + 1;
Node cand = rangeQuery(l, r);
vector<int> ids;
if (cand.c1 > 0) ids.push_back(cand.v1);
if (cand.c2 > 0 && cand.v2 != cand.v1) ids.push_back(cand.v2);
vector<ll> ans;
for (int id : ids) {
if (countInRange(id, l, r) >= need) ans.push_back(vals[id]);
}
sort(ans.begin(), ans.end());
if (ans.empty()) {
cout << -1 << '\n';
} else {
for (ll x : ans) cout << x << ' ';
cout << '\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 Node {
int v1 = -1, v2 = -1;
int c1 = 0, c2 = 0;
};
void addWeighted(Node &s, int x, int c) {
if (c <= 0) return;
if (s.c1 > 0 && s.v1 == x) {
s.c1 += c;
return;
}
if (s.c2 > 0 && s.v2 == x) {
s.c2 += c;
return;
}
if (s.c1 == 0) {
s.v1 = x;
s.c1 = c;
return;
}
if (s.c2 == 0) {
s.v2 = x;
s.c2 = c;
return;
}
int mn = min({s.c1, s.c2, c});
s.c1 -= mn;
s.c2 -= mn;
c -= mn;
if (s.c1 == 0) s.v1 = -1;
if (s.c2 == 0) s.v2 = -1;
if (c > 0) addWeighted(s, x, c);
}
Node mergeNode(Node a, const Node &b) {
if (b.c1 > 0) addWeighted(a, b.v1, b.c1);
if (b.c2 > 0) addWeighted(a, b.v2, b.c2);
return a;
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, q;
cin >> n >> q;
vector<ll> a(n), vals;
vals.reserve(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
vals.push_back(a[i]);
}
sort(vals.begin(), vals.end());
vals.erase(unique(vals.begin(), vals.end()), vals.end());
int m = (int)vals.size();
vector<int> comp(n);
vector<vector<int>> pos(m);
for (int i = 0; i < n; i++) {
comp[i] = lower_bound(vals.begin(), vals.end(), a[i]) - vals.begin();
pos[comp[i]].push_back(i + 1);
}
int sz = 1;
while (sz < n) sz <<= 1;
vector<Node> seg(2 * sz);
for (int i = 0; i < n; i++) {
seg[sz + i].v1 = comp[i];
seg[sz + i].c1 = 1;
}
for (int i = sz - 1; i >= 1; i--) {
seg[i] = mergeNode(seg[i << 1], seg[i << 1 | 1]);
}
auto rangeQuery = [&](int l, int r) {
Node left, right;
l += sz - 1;
r += sz - 1;
while (l <= r) {
if (l & 1) left = mergeNode(left, seg[l++]);
if (!(r & 1)) right = mergeNode(seg[r--], right);
l >>= 1;
r >>= 1;
}
return mergeNode(left, right);
};
auto countInRange = [&](int id, int l, int r) {
const vector<int> &p = pos[id];
return (int)(upper_bound(p.begin(), p.end(), r) - lower_bound(p.begin(), p.end(), l));
};
while (q--) {
int l, r;
cin >> l >> r;
int len = r - l + 1;
int need = len / 3 + 1;
Node cand = rangeQuery(l, r);
vector<int> ids;
if (cand.c1 > 0) ids.push_back(cand.v1);
if (cand.c2 > 0 && cand.v2 != cand.v1) ids.push_back(cand.v2);
vector<ll> ans;
for (int id : ids) {
if (countInRange(id, l, r) >= need) ans.push_back(vals[id]);
}
sort(ans.begin(), ans.end());
if (ans.empty()) {
cout << -1 << '\n';
} else {
for (ll x : ans) cout << x << ' ';
cout << '\n';
}
}
}
return 0;
}