题解 CF1713F【Lost Array】

先考虑第 \(0\) 行到第 \(n\) 列是怎么推的?

首先,为了方便将第 \(0\) 行的数从右往左重标号为 \(0, 1, \cdots, n - 1\)。我们发现 \((0, i)\) 对于 \((j,n)\) 的贡献是 \(C(i + j, i) \pmod 2\),根据 \(\text{lucas}\) 定理可得有贡献当且仅当 \(i\ \text{and}\ j = 0\)

考虑容斥,枚举 \(k\),钦定 \(i\ \text{and}\ j\) 在二进制下为 \(k\) 的超集,则 \((0,i)\)\((j,n)\) 被贡献次数为 \(2^{\text{popcount}(i\ \text{and} \ j)}\)。于是,直接异或即可。

所以,我们先考虑求出 \(t_i\) 表示 \(i\) 为超集的数的异或和(\(t\)\(a_{0,i}\) 的高维后缀异或和)。

考虑枚举 \(i\ \text{and}\ j = k\),于是,\(a_{j,n}\) 的值即为 \(\bigoplus\limits_{k\in j}^{n} t_k\)\(a_{j,n}\)\(t_{i}\) 的高维前缀异或和)。

综上,我们发现,从第 \(0\) 行推到第 \(n\) 列是对 \(a_{0,i}\) 先做一遍高维后缀异或和,再做一遍高维前缀异或和。

考虑从第 \(n\) 列如何反推第 \(0\) 行?

直接倒过来做,先对 \(a_{n,j}\) 做一遍高维前缀差分异或和,再做一遍高维后缀差分异或和。(因为是在异或运算下,所以前缀和和前缀差分、后缀和和后缀差分都分别是一个东西)。

const int N = 5e5 + 10;
int a[N];
signed main() {
	int n; read(n);
	for (int i = 0; i < n; ++i) read(a[i]);
	int lg = log2(n);
	F(i, 0, lg)
		for (int j = 0; j < n; ++j)
			if ((j >> i) & 1) a[j] ^= a[j - (1 << i)];
	F(i, 0, lg)
		for (int j = 0; j < n; ++j)
			if ((j >> i) & 1) a[j - (1 << i)] ^= a[j];
	for (int i = n - 1; ~i; --i) writes(a[i]);
	return 0;
}
posted @ 2022-12-09 21:03  zhaohaikun  阅读(44)  评论(0编辑  收藏  举报