题解 CF1713F Lost Array

第一步是如果给定 \(a\),怎么求 \(b\) 的最后一列。

考虑到 \(b_{i,j}=b_{i,j-1}\ \text{xor}\ b_{i-1,j}\),那么我们考虑最终 \(b_{i,n}\) 的贡献:对于每个 \(b_{0,j}\),设 \(b_{0,j}\)\(k\) 种不同走法(每一步向右或向下走,第一步必须向下)能到达 \(b_{i,n}\),那么 \(b_{0,j}(a_j)\)\(b_{i,n}\) 的贡献有 \(k\) 次。由于是异或,\(k\) 是奇数贡献为 \(a_j\);否则为 \(0\)

考虑计算 \(b_{0,j}\)\(b_{i,n}\) 有几种走法,运用组合数容易得到走法数为 \(\left(\begin{array}{c} i-1+n-j \\ i-1 \end{array}\right)\)。这个形式比较难受,我们考虑反转 \(a\),并且 \(a,b\) 下标都从 \(0\) 开始,式子变成了 \(\left(\begin{array}{c} i+j \\ i \end{array}\right)\)。根据 Lucas 定理(即 [CTSC2017]吉夫特 的经典结论),这个式子有贡献当且仅当 \((i+j)\ \text{and}\ i=i\),也容易转化为 \(i\ \text{and}\ j=0\)

将下标设成从 \(0\) 开始并反转 \(a\) 后,我们得到由 \(a\) 推出 \(b\) 的式子:\(b_i=\oplus_{i\ \text{and}\ j=0}\ a_j\)。我们可以用 FWT 或 FMT 在 \(O(n\log n)\) 的时间内由 \(a\) 计算出 \(b\),但这不是本题要求。

本题中已知 \(b\)\(a\),我们根据子集反演式子:

\[f(S)=\sum_{T \subseteq S} g(T) \Leftrightarrow g(S)=\sum_{T \subseteq S}(-1)^{|S|-|T|} f(T) \]

由于运算是异或,那么不用考虑 \(-1\) 的系数,也就是说对于一个数组 \(a\) 做一遍 FMT 得到 \(b\),那么也可以从 \(b\) 做一遍完全一致的 FMT 得到 \(a\)

遗憾的是本题还没有做完。你发现把 \(i\ \text{and}\ j=0\) 转化成子集形式后,\(b\) 数组的前几项未知,那么你也不能做反着的 FMT 从 \(b\) 推出 \(a\)。我场上也就到这里就止步了,所以没有做出此题。

不妨换一个思路。定义 \(c\)\(a\) 做一遍后缀 FMT 的结果,即 \(c_i\) 为所有 \(i\) 的超集 \(a\) 的异或和。考虑 \(i\ \text{and}\ j=0\)\(i\) 的所有位 \(j\) 都不能存在。我们容斥,枚举 \(i\) 的子集 \(k\),钦定这 \(k\) 位需要选,其他位随意,那么要计算的是满足 \(k\)\(j\) 的子集的所有 \(j\)(也就是 \(c_k\)),原本 \(-1\) 的容斥系数由于是异或也不需要考虑。

所以从 \(a\)\(b\) 得出了一个新的做法:先对 \(a\) 每个位置变成其超集的异或和,记这个新数组为 \(c\),再对 \(c\) 每个位置变成其子集的异或和,就可以得到 \(b\)

这个做法不会存在前一个做法“数组的前几项未知”的问题,因此从 \(b\) 反着做两个 FMT 回去就行了。

#include <bits/stdc++.h>
using namespace std;
int n,a[1000005];
int main()
{
	cin >> n;
	for (int i=0;i<n;i++) cin >> a[i];
	for (int i=0;i<19;i++)
	{
		for (int j=0;j<n;j++)
		{
			if (j&(1<<i)) a[j]^=a[j^(1<<i)];
		}
	}
	for (int i=0;i<19;i++)
	{
		for (int j=0;j<n;j++)
		{
			if (j&(1<<i)) a[j^(1<<i)]^=a[j];
		}
	}
	for (int i=n-1;i>=0;i--) cout << a[i] << " ";
	return 0;
}

小声:看官方题解不是很懂,特别感谢神仙 @chenxinyang2006 的指点,让我受益匪浅。

posted @ 2022-08-09 15:51  Little09  阅读(86)  评论(0编辑  收藏  举报