1 条题解
-
0
一、题意理解
我们有一个长度为 的数组 , 次询问。
对于询问 ,我们要统计在子数组 中,有多少个 不同的数值 ,满足:
在 中的出现次数 与 互质,即 。
注意:我们统计的是 不同的数值 的数量,而不是位置的数量。
二、样例分析
输入:
10 5 1 1 1 1 1 2 2 2 2 2 4 7 2 4 7 3 4 8 2 4 8 3 3 8 3数组:
1 1 1 1 1 2 2 2 2 2(下标从 1 开始)
询问 1:
区间 =1 1 1 2- 数值 1 出现次数 = 3
- 数值 2 出现次数 = 1
检查:
→ 1 符合
→ 2 符合
仔细看样例输出:
0 2 1 1 0第一个是 0,说明在区间 [4,7] 中,没有数值的出现次数与 2 互质。
区间 [4,7] 内:
- 1 出现 3 次(位置 4,5,6 是 1)。 ,,两个都互质。
三、一般解法思路
假设题目确实要求:
对于区间 ,设 表示数值 在区间内的出现次数,统计满足 的不同 的个数。
1. 暴力做法(不可行)
对每个询问,遍历区间统计每个数值的出现次数,然后检查 gcd,复杂度 ,太大。
2. 莫队算法
因为 次询问区间,可以离线用莫队处理。
- 我们维护每个数值在当前区间的出现次数 。
- 同时维护一个数组 表示出现次数为 的数值有多少个。
- 当移动指针时,更新 和 。
对于询问 ,我们需要计算: $ \text{ans} = \sum_{t \ge 1, \gcd(t,k)=1} count[t] $ 这里 是出现次数。
3. 如何快速计算
的范围最大为 ,但 非零的 不会太多(因为不同数值有限)。
在莫队移动时, 变化的 只有两个(旧频率和新频率),所以我们可以维护所有出现过的频率的集合。
对于每个 ,我们枚举 的所有质因子,用容斥原理计算与 互质的 对应的 之和。
具体: 设
我们要求: $ \sum_{\substack{t \\ \gcd(t,k)=1}} count[t] = \sum_{t} count[t] \cdot [\gcd(t,k)=1] $ 用莫比乌斯反演: $ [\gcd(t,k)=1] = \sum_{d|\gcd(t,k)} \mu(d) = \sum_{d|t, d|k} \mu(d) $ 所以: $ \text{ans} = \sum_{d|k} \mu(d) \sum_{t: d|t} count[t] $ 记 ,即所有 是 的倍数的 之和。
4. 维护
当某个数值的出现次数从 变为 :
- 对 的所有约数 , 减少 1
- 对 的所有约数 , 增加 1
由于 ,一个数的约数个数不多( 级别),所以每次更新是 。
5. 回答询问
对于给定的 ,枚举 的所有约数 ,计算: 这里 可以预处理。
四、算法步骤
- 预处理 1..n 的莫比乌斯函数 和每个数的约数。
- 读入数据,对询问按莫队顺序排序。
- 维护 、、。
- 移动指针时更新 。
- 回答询问时枚举 的约数求和。
五、复杂度
- 莫队移动 次,每次更新 需要 ,总 ,在 时可能稍大,但实际可过。
- 也可以先预处理每个 的约数,这样更新时直接遍历约数列表。
六、代码框架(C++)
#include <bits/stdc++.h> using namespace std; const int N = 50001; int a[N], b[N], c[N], d[N], ans[N]; int e, block, top, cnt; int sta[N], stp[N], g[N], zhi[N]; int sp[N], al; bool vis[N]; int vit[N]; struct Que { int l, r, k, x; } q[N]; inline bool kkk(Que x, Que y) { return b[x.l] != b[y.l] ? b[x.l] < b[y.l] : ((b[x.l] & 1) ? b[x.r]<b[y.r]: b[x.r]>b[y.r]); } inline void Insert(int x) { --d[c[x]]; ++c[x]; ++d[c[x]]; if (c[x] == block + 1) { if (!vis[x]) { vis[x] = 1; sta[++top] = x; } } } inline void sai() { for (int i = 2; i < N; ++i) { if (!g[i]) { g[i] = i; zhi[++cnt] = i; } for (int j = 1; j <= cnt; ++j) { if (i * zhi[j] >= N) { break; } g[i * zhi[j]] = zhi[j]; if (i % zhi[j] == 0) { break; } } } } inline void Delted(int x) { --d[c[x]]; --c[x]; ++d[c[x]]; } int main() { sai(); int n, m; scanf("%d%d", &n, &m); block = sqrt(n + m); for (int i = 1; i <= n; ++i) { b[i] = (i - 1) / block + 1; scanf("%d", &a[i]); } for (int i = 1; i <= m; ++i) { scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].k); q[i].x = i; } sort(q + 1, q + m + 1, kkk); int L = q[1].l, R = q[1].r; for (int i = L; i <= R; ++i) { Insert(a[i]); } for (int i = 1; i <= n; ++i) { if (__gcd(q[1].k, i) == 1) { ans[q[1].x] += d[i]; } } for (int i = 2; i <= m; ++i) { while (L > q[i].l) { Insert(a[--L]); } while (R < q[i].r) { Insert(a[++R]); } while (L < q[i].l) { Delted(a[L++]); } while (R > q[i].r) { Delted(a[R--]); } al = 0; while (g[q[i].k]) { if (vit[g[q[i].k]] != i) { vit[g[q[i].k]] = i, sp[++al] = g[q[i].k]; } q[i].k /= g[q[i].k]; } for (int j = 1; j <= block; ++j) { if (!d[j]) continue; bool flag = 1; for (int p = 1; p <= al; ++p) { if (j % sp[p] == 0) { flag = 0; break; } } ans[q[i].x] += (flag * d[j]); } int tot = 0; for (int j = 1; j <= top; ++j) { if (c[sta[j]] > block) { stp[++tot] = sta[j]; bool flag = 1; for (int p = 1; p <= al; ++p) { if (c[sta[j]] % sp[p] == 0) { flag = 0; break; } } ans[q[i].x] += flag; } else { vis[sta[j]] = 0; } } top = tot; for (int j = 1; j <= tot; ++j) { sta[j] = stp[j]; } } for (int i = 1; i <= m; ++i) { printf("%d\n", ans[i]); } return 0; }
- 1
信息
- ID
- 3916
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者