[CEOI2018] Lottery 题解

前言

题目链接:洛谷

题意简述

给出序列 \(a_1 \ldots a_n\) 和常数 \(l \leq n\),定义:

\[\operatorname{dis}(i, j) = \sum _ {k = 0} ^ {l - 1} [a_{i + k} \neq a_{j + k}] \qquad (i, j \in [1, n - l + 1]) \]

每次询问一个 \(k\),求对于所有 \(i \in [1, n - l + 1]\),求 \(\sum \limits _ {j \neq i} [\operatorname{dis}(i, j) = k]\)

题目分析

暴力不用说,考虑如何优化。这类问题我们思考能不能省略重复的计算。例如,对于 \(l_1 \sim r_1\)\(l_2 \sim r_2\)\(\operatorname{dis}\) 已经求出,那么对于 \(l_1 + 1 \sim r_1 + 1\)\(l_2 + 1 \sim r_2 + 1\)\(\operatorname{dis}\) 只需要在原来基础上减去 \([l_1 \neq l_2]\),再加上 \([r_1 + 1 \neq r_2 + 1]\)。是 \(\Theta(1)\) 的。

具体地讲,对于这两个区间,它们的差值的可能性是 \(\Theta(n)\) 的,我们枚举这个差值,然后将这两个区间向右平移,用上述算法计算,并累加。注意到这样会不重不漏地统计到每一个区间的答案。时间复杂度 \(\Theta(n ^ 2)\)

另外,由于特殊的空间限制,不妨将询问离线并离散化,空间复杂度就降到了 \(\Theta(nq)\)

代码

略去了快读快写。

#include <cstdio>
#include <algorithm>
using namespace std;

int n, m, q, val[10010];
int cnt[110][10010];
int qry[110], rq[110], who[10010];

signed main() {
	fread(buf, 1, MAX, stdin);
	read(n), read(m);
	for (int i = 1; i <= n; ++i) read(val[i]);
	read(q);
	for (int i = 1; i <= q; ++i) read(qry[i]), rq[i] = qry[i];
	sort(rq + 1, rq + q + 1);
	for (int i = 1; i <= q; ++i) who[rq[i]] = i;
	who[m + 1] = q + 1;
	for (int i = m; i >= 0; --i) !who[i] && (who[i] = who[i + 1]);
	for (int i = 1; i + m <= n; ++i) {  // 两个区间的差
		int tot = 0;
		for (int j = 1; j <= m; ++j) tot += val[j] != val[j + i];
		for (int l1 = 1, r1 = m, l2 = 1 + i, r2 = m + i; r2 <= n; ++l1, ++l2, ++r1, ++r2) {
			++cnt[who[tot]][l1], ++cnt[who[tot]][l2];
			tot -= val[l1] != val[l2];
			tot += val[r1 + 1] != val[r2 + 1];
		}
	}
	for (int i = 1; i <= q; ++i)
		for (int j = 1; j + m - 1 <= n; ++j)
			cnt[i][j] += cnt[i - 1][j];
	for (int i = 1; i <= q; ++i) {
		for (int j = 1; j + m - 1 <= n; ++j)
			write(cnt[who[qry[i]]][j]), putchar(' ');
		putchar('\n');
	}
	fwrite(obuf, 1, o - obuf, stdout);
	return 0;
}

后记 & 反思

考虑重复计算并优化是关键。另外,枚举两个区间的位置关系,并做到不重不漏的统计也是值得注意的。

posted @ 2024-07-22 17:06  XuYueming  阅读(10)  评论(0编辑  收藏  举报