| title | Mo's Algorithm | ||
|---|---|---|---|
| tags |
|
This method will be a key for solving offline range queries on an array. By offline, we mean we can find the answers of these queries in any order we want and there are no updates. Let’s introduce a problem and construct an efficient solution for it.
You have an array a with
First let’s find a naive solution and improve it. Remember the frequency array we mentioned before. We will keep a frequency array that contains only given subarray’s values. Number of values in this frequency array bigger than 0 will be our answer for given query. Then we have to update frequency array for next query. We will use
class Query {
public:
int l, r, ind;
Query(int l, int r, int ind) {
this->l = l, this->r = r, this->ind = ind;
}
};
void del(int ind, vector<int> &a, vector<int> &F, int &num) {
if (F[a[ind]] == 1) num--;
F[a[ind]]--;
}
void add(int ind, vector<int> &a, vector<int> &F, int &num) {
if (F[a[ind]] == 0) num++;
F[a[ind]]++;
}
vector<int> solve(vector<int> &a, vector<Query> &q) {
int Q = q.size(), N = a.size();
int M = *max_element(a.begin(), a.end());
vector<int> F(M + 1, 0); // This is frequency array we mentioned before
vector<int> ans(Q, 0);
int l = 0, r = -1, num = 0;
for (int i = 0; i < Q; i++) {
int nl = q[i].l, nr = q[i].r;
while (l < nl) del(l++, a, F, num);
while (l > nl) add(--l, a, F, num);
while (r > nr) del(r--, a, F, num);
while (r < nr) add(++r, a, F, num);
ans[q[i].ind] = num;
}
return ans;
}Time complexity for each query here is
We will change the order of answering the queries such that overall complexity will be reduced drastically. We will use following cmp function to sort our queries and will answer them in this sorted order. Block size here is
bool operator<(Query other) const {
return make_pair(l / block_size, r) <
make_pair(other.l / block_size, other.r);
}Why does that work? Let’s examine what we do here first then find the complexity. We divide
int block_size;
class Query {
public:
int l, r, ind;
Query(int l, int r, int ind) {
this->l = l, this->r = r, this->ind = ind;
}
bool operator<(Query other) const {
return make_pair(l / block_size, r) <
make_pair(other.l / block_size, other.r);
}
};
void del(int ind, vector<int> &a, vector<int> &F, int &num) {
if (F[a[ind]] == 1) num--;
F[a[ind]]--;
}
void add(int ind, vector<int> &a, vector<int> &F, int &num) {
if (F[a[ind]] == 0) num++;
F[a[ind]]++;
}
vector<int> solve(vector<int> &a, vector<Query> &q) {
int Q = q.size(), N = a.size();
int M = *max_element(a.begin(), a.end());
block_size = sqrt(N);
sort(q.begin(), q.end());
vector<int> F(M + 1, 0); // This is frequency array we mentioned before
vector<int> ans(Q, 0);
int l = 0, r = -1, num = 0;
for (int i = 0; i < Q; i++) {
int nl = q[i].l, nr = q[i].r;
while (l < nl) del(l++, a, F, num);
while (l > nl) add(--l, a, F, num);
while (r > nr) del(r--, a, F, num);
while (r < nr) add(++r, a, F, num);
ans[q[i].ind] = num;
}
return ans;
}