[专题总结]FWT 及 位运算相关
Xor相关工具
线性基,FWT,数位dp,按位确定,01Tire。
线性基在 这篇博客里面 , 其他的主要是应用就不再赘述。
FWT
Pre
FWT 主要是异或性质比较多,可以独立出题。
高维前缀和,高维后缀和的 FWT 只能配合其他观察,做法,当做优化的工具,原理简单,故不提,只放相关题目。
异或卷积
定义运算 \(x \ominus y = |x\&y|\%2\) , 发现 \(\&\) 运算是独立的,等价与每位上与,每位上的与又等价于乘法,故这里以乘法代替与。
\(x \ominus y = \prod_{bit=1}^{len} x_i * y_i\) , \(x_i\) 是 \(x\) 的第 \(i\) 位。
- 性质: \((x \ominus y) \oplus (x \ominus z) = x\ominus (y \oplus z)\) ,即异或对 \(\ominus\) 有分配率
- 证明: \((x \ominus y) \oplus (x \ominus z) = \prod_{bit=1}^{len} x_i * y_i \oplus \prod_{bit=1}^{len} x_i * z_i=\prod_{bit=1}^{len} x_i *( z_i \oplus y_i) = x\ominus (y \oplus z)\)
\(\rm FWT\) 是对一个序列的线性变换,每个位置都会以 \(1/-1\) 的系数贡献给任意一个位置。
\(\rm FWT\) 变换矩阵是满秩的,故存在 \(\rm IFWT\) (\(\rm FWT\) 的逆变换)
计算异或卷积 \(\displaystyle C_i=\sum_{j \oplus k = i} A_j *B_k\) , 只需要把 \(A,B\) FWT,点值相乘再 IFWT 回去。
也就是,我们需要证明 \(\displaystyle {\rm FWT}(A)_i* {\rm FWT}(B)_i = {\rm FWT}(C)_i\), 使用 \(\ominus\) 的性质和和式变换即可。
子集卷积
解决 \(\displaystyle A_i=\sum_{j\subseteq i} B_j*C_{i\oplus j}\) 的问题,也是不交集合并卷积。
发现限制等价于 \(|j|+|k|=|i|, j|k=i\) , 故加上占位多项式 \(f_{i}\) 代表所有大小是 \(|i|\) 的集合的东西,和或卷积一样做了。
注意到他是按大小从小到大做的,所以可以自己卷自己,只需要枚举大小每次分别卷即可。
具体参考 cmd 博客,感觉卷积部分意义不大因为CCF钦定不考,所以只是口胡一些题理解思想。
题目
[JOI2018] Snake Escaping
显然存在一种 \(O(2^{|?|})\) 的做法, \(|?|\) 是问号集合的大小,即暴力替换每个问号。
如果我们有 \(O(2^{|0|}), O(2^{|1|})\) 的做法,就可以进行数据分治。
算是根号分治的一种扩展吧,不要局限于拼接两种算法
考虑 \(2^{|0|}\) 的做法,我们可以枚举一些 \(0\) ,去强制这些位置不是 \(0\), 然后容斥。
接下来问题就变成了一些位置强制是 \(1\), 一些位置是啥都行的和,高维前后缀和处理出来, \(2^{|1|}\) 做法同理。
CF1336E1
首先先把他插到线性基里面,对线性基里所有的数计算答案,最后乘 \(2^{n-|B|}\) 即可, 因为线性基内每个元素都可以被表示 \(2^{n-|B|}\) 次,证明见 albus 出场
现在 \(n=35\), 并且还有一个重要的性质,低位上面的数不会影响高位上面的数,这是线性基带来的关键性质。
有两种做法, Dp 和 MIM(meet in the middle)。
\(DP_{i,j,k}\) 代表考虑到第 \(i\) 位,\(>i\)的位的1的个数是 \(j\), 选的数异或出来的数的后 \(i\) 位是 \(k\) 的方案数。
转移考虑选不选当前数, \(O(1)\) 转移,复杂度就是状态数。
之所以可以这么记录状态,是因为低位的数不会影响到高位的数,所以并不需要记录高位的数是啥,只需要记录高位有几个1即可。
状态数是 \(O(2^{35/2}*35*35)\) 的,因为 \(i \geq 17\) 的时候,只选了 $ \leq 17$ 个数,所以最后一维只有 \(2^17\) 个状态, \(i<17\) 的时候,虽然选的数多了,但是后 \(i\) 位只有 \(2^{35-17}\) 种可能,所以状态数是合法的。
MIM 需要同样的思想,拆成高位和低位,分别先贡献到答案里面,现在只需要考虑高位和低位配对的贡献。
由于低位并不会改变高位的值,所以只需要记录高位的数的后面17位,在记录一下前面的位上有多少个1的方案数。
合并使用 \(\rm FWT\) 即可。
CF1336E2
先咕了
ABC215H
首先第一问很简单,直接 Hall 定理就可以知道,我们需要对所有破坏 Hall 定理需要的东西取 \(\rm min\)
问题转化成了,给定 n 个可以区分的球,和每个球属于的一个袋子,又给定了若干个袋子的集合,要求你选 \(k\) 个球 (k 给定),保证这 \(k\) 个球的袋子组成的集合是给出的若干个袋子集合的中至少一个集合的子集。
m(袋子) \(\leq 20\), 集合 \(\leq 2^{20}\)。
他的限制是“若干个袋子集合的中至少一个集合的子集”, 相当于题目给了若干了球的集合,要求你选的球必须是其中一个集合的子集,你在每个集合内都计算一遍,交集部分会算重复,考虑容斥。
我们枚举一个袋子集合的集合,计算在这些集合的交集内选择 \(k\) 个数的方案数,在乘上 \((-1)^{sz-1}\) , 复杂度 \(2^{2^m}\), 考虑优化,就是对每一种交集算出容斥系数之和,属于容斥的经典优化了。
问题变成了给定若干个数,求选择任意多个数与起来等于 \(x\) 的带权方案数,权是选择了奇数个数就是1, 偶数个数就是 -1。
要算带权方案数,不如先考虑方案数怎么计算。
选若干个数,与=x, 那么这些数必须都是 \(x\) 的超集,然后选择的方案数是 \(2^{cnt}\), \(cnt\) 可以通过高维后缀和快速求出,选出来的意义是选择若干个数,与出来是 \(x\) 的超集的方案数,那么再高维后缀差分一下就可以得到答案。
带权方案数同理扩展,计算选择若干个数是 \(x\) 的超集的带权方案数再差分,带权方案数就是 \([cnt==0]\)
别忘记容斥的本质:集合的并转集合交
ABC220H
\(n \leq 40\), 可以考虑折半搜索, 设 $ \leq 20$ 的点集是 \(S\), 剩下的部分是 \(T\) 。
图中的边分成 \(S,T\) 内部的边和 \(S,T\) 之间的边。
设 $F_s, (s\subseteq S) $ 代表 \(s\) 集合覆盖的边数。
$G_t, (t\subseteq T) $ 代表 \(t\) 在其导出子图内覆盖的边数。
那么还剩下 \(T\) 和 \(S/s\) 之间的连边,我们只用考虑连边为奇数的点,偶数的不对答案影响。
把合法条件表示成柿子是关键一步
设 \(Z_s, (s\subseteq S)\) 代表 \(S/s\) 和 \(T\) 中连边为奇数的点的集合,是一个 \(T\) 的集合。
那么 \(F_s \oplus G_t \oplus ((T\& Z_s)\&1) =0\) , \(s,t\) 就是合法的。
用 \(K_s\) 代表 \(Z_{t,0/1} = s, F_t=0/1\) 的个数,然后枚举 \(T\& Z_s\)分类讨论做与卷积即可。
CF1326F2
一个显然的暴力:对于每个串都 \(dp_{x,i}\) 代表当前选的集合是 \(x\), 在第 \(i\) 行的方案数。
正解的话,这道题第一次见确实不知道怎么入手,我略微总结一下 :
信息只存在于元素的间隔的时候,可以考虑枚举划分数,归成等价类
所以现在我们呢想到了枚举划分数,那么这个序列就被我们划分成了若干个 \(1\) 的连续段。
我们只关心连续段的长度和这个长度的连续段的数量,但是有一个问题,就是 \(0\) 隔开的不能是 \(1\), 这个限制很麻烦。
考虑对这个限制进行容斥,我们计算每个数, \(0\) 代表不限制, \(1\) 代表强制是 \(1\), 统计其方案数,本质是统计的后缀和。
经典子集反演,做一个 \(\rm FMT\) 就可以达到完成子集反演的效果。
贴一个 FMT 的板子
void FwtOR(int f[N], int n){
n = __builtin_ctz(n);
for(R j = 0; j < n; j++)
for(R i = 0; i < (1 << n); i++)
if((i >> j) & 1) jA(f[i], f[i ^ (1 << j)]);
}
void IFwtOR(int f[N], int n){
n = __builtin_ctz(n);
for(R j = 0; j < n; j++)
for(R i = (1 << n); ~i; i--)
if((i >> j) & 1) jA(f[i], P - f[i ^ (1 << j)]);
}
void FwtAND(int f[N], int n){
n = __builtin_ctz(n);
for(R j = 0; j < n; j++)
for(R i = (1 << n) - 1; ~i; i--)
if((i >> j) & 1) jA(f[i ^ (1 << j)], f[i]);
}
void IFwtAND(int f[N], int n){
n = __builtin_ctz(n);
for(R j = 0; j < n; j++)
for(R i = 0; i < (1 << n); i++)
if((i >> j) & 1) jA(f[i ^ (1 << j)], P - f[i]);
}
思想很简单,先枚举维,然后一维咋做他咋做。
接着回到本题,我们容斥完以后,就只需要算这种划分的方案数了。
那我们对于每种划分都做一遍暴力 \(Dp\) 即可,在 Dfs 的时候顺便子集卷积,乘上这个集合的 \(DFT\) 值接着搜,搜到每个地方 \(IDFT\) 回来得到答案,复杂度 \(2^n*(P(n)+n^2)\)
NOI模拟6.4 T1
有了上面那一道题的铺垫,这道题(容斥+枚举划分)就很显然了。
然后我们观察一个重要的性质,这个东西可以划分成若干条极长的链,并且最长的链长度不超过 \(6\) 。
那么我们把每个极长的链找出来,记录划分数为状态,在每条极长的链暴力枚举划分去转移,状态数 \(p(n)\) , 最大转移 \(p(6)\) ,稳稳通过。
感觉先处理长一点的链会减少常数,因为转移多的状态对应少。
CF914G
先对每个 \(S\) 算出来 \(a | b=S,a\&b=0\) , \(\sum fib_a*fib_b\) 的和。
再对每个 \(S\) 算出来 \(a \oplus b = S\) , \(\sum fib_a*fib_b\) 的和。
然后经典值域 \(FWT\), 把 \(Fib\) 和上面那两个一起卷起来,最后取所有 \(2^i\) 的点值。
写题解的意义是别忘记 \(FWT\) 可以同时卷很多东西而不止两个。
CF1119H类
感觉不必复读一遍题解,大体就是做 \(FWT\), 然后求一个地方的点值, 在 \(IFWT\) 回去得到答案。
关键在于如何求一个地方的点值,发现是一堆 \((-1)^{?}*x+(-1)^?*y+(-1)^?*z\) 乘起来,自然想对每种情况计算系数。
然后我们考虑简单点, 假如只有一项即 \((-1)^?*x\), 怎么计算 \(x, -x\) 分别有多少个?
一个经典的求 \(A,B\) 的值的方法就是 知道 \(A+B,A-B\)
一般 \(A,B\) 对应是某某某 奇数/偶数 的方案数,总和一般容易知道, \(A-B\) 一般通过容斥得到。
在本题内,可以发现容斥这步只需要一个 \(\rm FWT\) 就可以解决,所以本题关键在于想到去解方程。
然后在考虑 \(k=3\) 扩展到任意,发现我们接的方程就是 \(\rm FWT\) 的矩阵 (不知道咋发现的),所以直接做 \(\rm IFWT\) 变换就行了。
省选模拟12C
题意: 统计有多少个长度为 \(L\) 的序列,满足 \(L\) 的元素全部由 \(S\) 集合构成,且 \(L\) 的任意一个前缀的异或和不属于 \(T\) 集合, 对 $ 998244353$ 取模, 数据范围 $ L \leq 4e3, |S|\leq 35, |T| \leq 20$ ,保证 \(S_i,T_i\) 互不相同
求出 \(G_{i,j,k}\) 代表 \(i\) 个 \(S\) 集合中的数,异或出来 \(T{j} \oplus T{k}\) 的方案数。
对于比较小的 \(S_i,T_i\) ,有一个用 \(FWT\) 计数的经典套路,值域 \(FWT\) 。
在每个 \(S_i\) 对应的值域位置 ++
,然后做一下 \(DFT\) 变换,此时, \(x\) 处的值就是 \(\sum_{j=1}^n (-1)^{|x \&j|} *a_j\) ,我们给系列点乘一下自身,在 \(IDFT\) 回去, \(x\) 处的值就是有多少对 \(i \oplus j=x\)。
同理的,如果我们给 \(x\) 自身做 \(x^k\) ,也就是把 \(k\) 个 值域上的计数数组异或卷积到一起, \(x\) 处的值就是 \((\oplus_{i=1}^{k} b_i)=x\) 的 \(b\) 序列个数,这也是经典异或计数思路.
我们并不能做多次 \(IDFT\) ,这样复杂度过于高,注意到我们只需要 \(IDFT\) 一个地方的值,所以可以直接用原始公式。
\(IDFT(a)[x]=\frac{\sum_{i=1}^n(-1)^{|x \& i|}}{n}*a_j\) ,就是 \(DFT\) 除以 \(n\) 得到的,对 \(DFT\) 矩阵求逆可得。
这样对于 \(m^2\) 个数的每个 \(L\) 都做一遍 \(O(值域)\) 扫描就可以得出,但是还是不够。
发现 \(S_i\) 不相同,所以值域数组上每个地方的值域是 \([-1,1]\) , \(DFT\) 之后,因为有值的地方只有 \(|S|\) 个,所以 \(DFT\) 数组值域是 \([-|S|,|S|]\) 的,这不大,可以对每对 \(m^2\) 的数, \(O(值域)\) 扫一遍数组,求出每个值有多少个,然后个 \(L\) ,只需要 \(O(|S|)\) 扫一遍桶就可以得到 \(IDFT\) 的值,就成功解决了问题。
但是题目的值域是 \(1e9\) ,我们没有方法去扫一遍值域,这个时候可以使用经典套路: 线性基状压缩小值域,具体的,我们把原序列每个数都塞到线性基内,然后由于线性基可以表示这个序列,所以我们把线性基内元素状压,一个二进制的数代表一个线性基内的集合异或出来的数,这样可以达到减少值域的效果。
但是本题, \(|S|\leq 35\), $ 2^{35}$比 $ 1e9$ 还大,压缩个寂寞?
我们分情况讨论一下,如果线性基内元素 \(\leq 17\) ,就去线性基压缩+值域FWT统计。
否则说明不在线性基上的元素 \(\leq 17\) ,那么我们直接枚举一个不在线性基上的集合,根据线性基可以求出线性基上的元素集合,也就是一种一种方案暴力统计,复杂度是 \(O(m^22^{n/2})\) .
省选模拟16 A
题意: 给一个 \(n*m\)