1 条题解
-
0
题意
给定一个数组 ,有多个查询 ,需要判断每个查询区间内是否存在一个数值 ,使得该数值在区间内恰好出现 次。
思路
本题可以用更简单的 解法,但这里将介绍一种 的离线算法。
我们将所有查询离线处理。对于每个右端点 (),记录所有以 结尾的查询。然后从左到右遍历 ,同时维护一个数组 ,使得对于当前 , 就是查询 的答案。
为了保持 的正确性,在处理所有以 结尾的查询之前,需要先更新 。设当前数值 ,并设向量 为数值 之前出现位置的下标列表(下标从 开始)。- 如果 ,则需要在 上加 ,因为这个位置是从右往左第一个恰好包含 个 的位置。
- 之后,如果 ,则需要在 上减 ,以关闭当前区间并取消之前的贡献。
- 最后,如果 ,则还需要在 上加 ,以取消之前对区间的关闭操作。
算法步骤
- 将所有查询按右端点 分组,存入列表。
- 初始化数组 全为 ,并初始化每个数值的出现位置列表 为空。
- 从左到右遍历 从 到 :
- 设 ,将 加入 对应的位置列表 。
- 根据上述规则更新 数组:
- 若 ,则 。
- 若 ,则 。
- 若 ,则 。
- 对于所有以 为右端点的查询 ,计算 作为答案。
- 输出所有查询的答案。
复杂度分析
- 时间复杂度:,其中 为数组长度。每次更新 和查询区间和均可用树状数组或线段树实现 操作。
- 空间复杂度:,用于存储 数组、位置列表和查询分组。
代码说明
- 使用树状数组(Fenwick Tree)维护 的区间和,支持单点更新和前缀和查询。
- 遍历 时,对每个数值 维护其出现位置列表,按上述规则进行三次更新。
- 对于每个查询 ,通过树状数组计算 得到答案。
#include <cstdio> struct { inline operator int () { int x; return scanf("%d", &x), x; } } read; const int maxn = 100005, maxb = 500; int a[maxn], tot[maxn]; int t[maxb][maxn], val[maxb]; int main () { int n = read, q = read; for (int i = 1; i <= n; i ++) if ((a[i] = read) <= n) ++ tot[a[i]]; int p = 0; for (int x = 1; x <= n; x ++) if (tot[x] >= x) { val[++ p] = x; for (int i = 1; i <= n; i ++) t[p][i] = t[p][i - 1] + (a[i] == x); } while (q --) { int l = read, r = read, ans = 0; for (int i = 1; i <= p; i ++) if (t[i][r] - t[i][l - 1] == val[i]) ++ ans; printf("%d\n", ans); } }
- 1
信息
- ID
- 6720
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者