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);
}

结语

哈希大法好.

posted @ 2025-03-13 20:31  Steven1013  阅读(16)  评论(0)    收藏  举报