题解 CF1713F Lost Array

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

考虑到 bi,j=bi,j1 xor bi1,j,那么我们考虑最终 bi,n 的贡献:对于每个 b0,j,设 b0,jk 种不同走法(每一步向右或向下走,第一步必须向下)能到达 bi,n,那么 b0,j(aj)bi,n 的贡献有 k 次。由于是异或,k 是奇数贡献为 aj;否则为 0

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

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

本题中已知 ba,我们根据子集反演式子:

f(S)=TSg(T)g(S)=TS(1)|S||T|f(T)

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

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

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

所以从 ab 得出了一个新的做法:先对 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 @   Little09  阅读(92)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示