You have to analyze some array of \$n\$ numbers. In particular, you are interested in the so-called array uniformity. Suppose that in the array the number \$b_1\$ appears \$k_1\$ times, \$b_2\$ \$k_2\$ times, etc. Then the uniformity of an array is the minimum integer \$c \geq 1\$ such that \$c \leq k_i\$ for any \$i\$.
As part of your research, you want to perform \$q\$ operations in sequence.
- Operation \$t_i = 1, l_i, r_i\$ specifies a research request. It is necessary to deduce the uniformity of an array consisting of elements in positions from \$l_i\$ to \$r_i\$ inclusive.
- Operation \$t_i = 2, p_i, x_i\$ specifies a request for data clarification. Starting from this point in time, the \$p_i\$th element of the array is assigned the value \$x_i\$.
Input format: The first line contains \$n\$ and \$q\$ (\$1 \leq n, q \leq 100\,000\$) - the size of the array and the number of queries, respectively. The second line contains exactly \$n\$ numbers - \$a_1, a_2, \ldots, a_n\$ (\$1 \leq a_i \leq 10^9\$). Each of the remaining \$q\$ lines specifies another query. The first type of query is specified by three numbers \$t_i = 1, l_i, r_i\$, where \$1 \leq l_i \leq r_i \leq n\$ are the boundaries of the corresponding segment. The second type of query is specified by three numbers \$t_i = 2, p_i, x_i\$, where \$1 \leq p_i \leq n\$ is the position at which the number needs to be replaced, and \$1 \leq x_i \leq 10^9\$ - its new meaning. Output format: For each query of the first type, print one number - the uniformity of the corresponding array segment.
Example
10 4
1 2 3 1 1 2 2 2 9 9
1 1 1
1 2 8
2 7 1
1 2 8
Answer:
2
3
2
Note: The first query consists of exactly one element - \$1\$. The minimum suitable \$c = 2\$. The second query segment consists of four \$2\$, one \$3\$ and two \$1\$. The minimum suitable \$c = 3\$. The segment of the fourth query consists of three \$1\$, three \$2\$ and one \$3\$. The minimum suitable \$c = 2\$.
My very first code:
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
using namespace std;
const int MAXN = 100000;
int n, q;
int a[MAXN];
int find_uniformity(const vector<int>& segment) {
unordered_map<int, int> freq;
for (int num : segment) {
freq[num]++;
}
unordered_set<int> frequency_set;
for (const auto& kv : freq) {
frequency_set.insert(kv.second);
}
int c = 1;
while (frequency_set.find(c) != frequency_set.end()) {
c++;
}
return c;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> q;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < q; i++) {
int t;
cin >> t;
if (t == 1) {
int l, r;
cin >> l >> r;
l--; r--;
vector<int> segment(a + l, a + r + 1);
int result = find_uniformity(segment);
cout << result << endl;
} else if (t == 2) {
int p, x;
cin >> p >> x;
p--;
a[p] = x;
}
}
return 0;
}
But since it was running slow, I tried to optimize it using a segment tree, which is used to store the frequency of each element in a segment using an array of size MAXV + 1 (where MAXV is the maximum possible value in the array)
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
using namespace std;
const int MAXN = 100000;
const int MAXA = 1000000000;
const int MAXV = 10;
struct SegmentTree {
vector<vector<int>> tree;
int size;
SegmentTree(int n) {
size = n;
tree.resize(4 * n, vector<int>(MAXV + 1, 0));
}
void build(const vector<int>& arr, int node, int start, int end) {
if (start == end) {
tree[node][arr[start]]++;
} else {
int mid = (start + end) / 2;
build(arr, 2 * node + 1, start, mid);
build(arr, 2 * node + 2, mid + 1, end);
merge(tree[node], tree[2 * node + 1], tree[2 * node + 2]);
}
}
void merge(vector<int>& parent, const vector<int>& left, const vector<int>& right) {
for (int i = 1; i <= MAXV; i++) {
parent[i] = left[i] + right[i];
}
}
void update(int node, int start, int end, int idx, int old_val, int new_val) {
if (start == end) {
tree[node][old_val]--;
tree[node][new_val]++;
} else {
int mid = (start + end) / 2;
if (idx <= mid) {
update(2 * node + 1, start, mid, idx, old_val, new_val);
} else {
update(2 * node + 2, mid + 1, end, idx, old_val, new_val);
}
merge(tree[node], tree[2 * node + 1], tree[2 * node + 2]);
}
}
vector<int> query(int node, int start, int end, int l, int r) {
if (r < start || end < l) {
return vector<int>(MAXV + 1, 0);
}
if (l <= start && end <= r) {
return tree[node];
}
int mid = (start + end) / 2;
auto left = query(2 * node + 1, start, mid, l, r);
auto right = query(2 * node + 2, mid + 1, end, l, r);
vector<int> result(MAXV + 1, 0);
merge(result, left, right);
return result;
}
};
int find_uniformity(const vector<int>& freq) {
unordered_set<int> frequency_set;
for (int f : freq) {
if (f > 0) frequency_set.insert(f);
}
int c = 1;
while (frequency_set.find(c) != frequency_set.end()) {
c++;
}
return c;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, q;
cin >> n >> q;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
SegmentTree seg_tree(n);
seg_tree.build(a, 0, 0, n - 1);
for (int i = 0; i < q; i++) {
int t;
cin >> t;
if (t == 1) {
int l, r;
cin >> l >> r;
l--; r--;
auto freq = seg_tree.query(0, 0, n - 1, l, r);
int result = find_uniformity(freq);
cout << result << endl;
} else if (t == 2) {
int p, x;
cin >> p >> x;
p--;
int old_val = a[p];
a[p] = x;
seg_tree.update(0, 0, n - 1, p, old_val, x);
}
}
return 0;
}
It’s strange, but the code didn’t speed up significantly, as I would have liked, and I don’t know any other optimization algorithm in this case. Is it possible to somehow optimize this code or find a different algorithm?
It is worth noting that Fenwick Tree and Mo's Algorithm do not solve this problem, I tried...
#include <iostream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
using namespace std;
const int MAXN = 100000;
struct FenwickTree {
vector<int> tree;
int size;
FenwickTree(int n) {
size = n;
tree.resize(n + 1, 0);
}
void update(int index, int delta) {
for (++index; index <= size; index += index & -index) {
tree[index] += delta;
}
}
int query(int index) {
int sum = 0;
for (++index; index > 0; index -= index & -index) {
sum += tree[index];
}
return sum;
}
int range_query(int left, int right) {
return query(right) - query(left - 1);
}
};
int find_uniformity(const unordered_map<int, int>& freq_map) {
unordered_set<int> frequency_set;
for (const auto& kv : freq_map) {
frequency_set.insert(kv.second);
}
int c = 1;
while (frequency_set.find(c) != frequency_set.end()) {
c++;
}
return c;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, q;
cin >> n >> q;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
FenwickTree fenwick(MAXN);
unordered_map<int, int> freq;
for (int i = 0; i < n; i++) {
freq[a[i]]++;
fenwick.update(a[i], 1);
}
for (int i = 0; i < q; i++) {
int t;
cin >> t;
if (t == 1) {
int l, r;
cin >> l >> r;
l--; r--;
unordered_map<int, int> segment_freq;
for (int j = l; j <= r; j++) {
segment_freq[a[j]]++;
}
int result = find_uniformity(segment_freq);
cout << result << endl;
} else if (t == 2) {
int p, x;
cin >> p >> x;
p--;
fenwick.update(a[p], -1);
if (--freq[a[p]] == 0) {
freq.erase(a[p]);
}
a[p] = x;
fenwick.update(a[p], 1);
freq[a[p]]++;
}
}
return 0;
}