2025.03.13 CW 模拟赛 C. 统计
C. 统计
思路
考场上想到了 \(\displaystyle \mathcal{O}(\frac{n^2}{m})\) 的做法. 因为合法区间只有 \(\displaystyle \frac{n}{m}\) 种不同的长度, 因此只需要枚举 \(\displaystyle \frac{n^2}{m}\) 个区间, 每个区间 \(\mathcal{O}(1)\) 判断即可.
再考虑一种 \(\mathcal{O}(nm)\) 的做法. 具体来说, 我们用一个 vector 存储以 \(i\) 结尾的前缀中每个数出现的次数. 我们对数组进行差分 \((\)例如对于每一个数减去第一个数的出现次数, 形成相对差值\()\). 这样, 我们枚举每一个右端点 \(r\), 区间 \([l, r]\) 合法当且仅当 \(l\) 和 \(r\) 位置的差分数组相等.
正解利用和哈希. 总的来说, 就是将每一个数 \(i\) 赋一个随机值, 区间的哈希值就等于其中每个数的哈希值的总和. 注意, 所有数的哈希值总和为 0. 这样, 计算合法区间数量就等价于计算与 \(r\) 哈希值相等的左端点有多少个了.
不过这道题似乎卡 unordered_map? 时间复杂度 \(\mathcal{O}(n + m)\).
void init() {
mt19937 rnd(time(NULL));
mp.clear(), mp[0] = 1;
read(n, m);
for (int i = 1; i <= n; ++i) read(a[i]);
ll t = 0;
for (int i = 1; i < m; ++i) t -= (v[i] = (rnd() + 1) * (rnd() + 1ll));
v[m] = t;
}
void calculate() {
ll ans = 0, t = 0;
for (int i = 1; i <= n; ++i) {
t += v[a[i]];
cout << t << '\n';
ans += mp[t]++;
}
printf("%lld\n", ans);
}
结语
哈希大法好.

浙公网安备 33010602011771号