📅  最后修改于: 2023-12-03 15:11:40.710000             🧑  作者: Mango
本文介绍了MO的算法,一种用于解决类似于给定范围内的数组乘积除数查询这样的问题的算法。其基本思想是通过离线处理和莫队算法,将查询操作转化为对区间操作的统计,从而实现快速查询。
莫队算法是一种用于处理查询区间的算法,其基本思想是将查询离线,然后通过移动区间的左右端点,每次只修改少量元素,从而实现查询和修改的效率。下面是莫队算法的具体实现过程:
在给定范围内的数组乘积除数查询问题中,我们需要查询所有区间内,乘积拥有某个特定的因子的数量。我们可以将该问题转化为对每个区间内求以特定因子$a$为底的对数$c$是否为整数的数量。这可以通过将区间内所有数字质因数分解后,统计特定因子$a$在各个数字中出现的次数,然后统计区间内所有数字在该因子上的贡献之和,判断是否为$c$。
MO的算法可以通过莫队算法实现:我们将所有数字分解质因数,然后为每个查询设置一个根据质因数分解记录查询结果的桶数组。在每次莫队算法的操作中,对于加入或删除的数字的每个质因数,更新它们在该查询的结果中的贡献。最终我们可以通过查找目标因子在该查询结果中的贡献和,得到该查询的结果。
下面是MO的算法的具体实现过程:
const int N = 1e5 + 10;
const int M = 320;
int n, q;
int a[N];
int cnt[M][N];
int l[M], r[M], pos[N];
struct Query {
int id, l, r, a;
} Q[N];
bool cmp(const Query& x, const Query& y) {
if (pos[x.l] != pos[y.l]) return pos[x.l] < pos[y.l];
if (pos[x.l] & 1) return x.r < y.r;
else return x.r > y.r;
}
void add(int i, int& res) {
int t = a[i];
for (int d = 2; d * d <= t; d++) {
while (t % d == 0) {
t /= d;
cnt[pos[i]][d]++;
if (cnt[pos[i]][d] == 1) res++;
}
}
if (t > 1) {
cnt[pos[i]][t]++;
if (cnt[pos[i]][t] == 1) res++;
}
}
void del(int i, int& res) {
int t = a[i];
for (int d = 2; d * d <= t; d++) {
while (t % d == 0) {
t /= d;
cnt[pos[i]][d]--;
if (cnt[pos[i]][d] == 0) res--;
}
}
if (t > 1) {
cnt[pos[i]][t]--;
if (cnt[pos[i]][t] == 0) res--;
}
}
int gcd(int a, int b) {
if (a == 0) return b;
return gcd(b % a, a);
}
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int block = sqrt(n);
for (int i = 1; i <= q; i++) {
int l, r, a;
scanf("%d%d%d", &l, &r, &a);
Q[i] = {i, l, r, a};
pos[i] = i / block;
}
sort(Q + 1, Q + 1 + q, cmp);
int L = 1, R = 0, res = 0;
for (int i = 1; i <= q; i++) {
while (L > Q[i].l) add(--L, res);
while (R < Q[i].r) add(++R, res);
while (L < Q[i].l) del(L++, res);
while (R > Q[i].r) del(R--, res);
int h = gcd(a, Q[i].a);
printf("%d\n", res - cnt[Q[i].id][h]);
}
return 0;
}
MO的算法通过利用莫队算法将查询区间操作离线处理,通过记录每个数字的质因数分解结果,实现了高效统计乘积含有给定因子的查询。其时间复杂度为$O(n\sqrt{n}\log{W})$,其中$W$为数组内数字的最大值。