快速沃尔什变换(FWT)快速莫比乌斯变换(FMT)

虽然一点不懂,但是看着代码短得感人,背过就好了吧。

\[FWT(A) = (FWT(A_0), FWT(A_1 + A_0)) \]

\[IFWT(A) = (IFWT(A_0), IFWT(A_1 - A_0)) \]

\[FWT(A) = (FWT(A_0 + A_1), FWT(A_1)) \]

\[IFWT(A) = (IFWT(A_0 - A_1), IFWT(A_1)) \]

异或

\[FWT(A) = (FWT(A_0 + A_1), FWT(A_0 - A_1)) \]

\[FWT(A) = (FWT(\frac{A_0 + A_1}{2}), FWT(\frac{A_0 - A_1}{2})) \]

代码实现

记忆

有些东西不用全靠硬背,可以有技巧地背

进行或运算,通常数会往大里走,所以较高位的数要加上较低位的数 (雾)

与 与 或 相反,通常数会往小里走,于是 是较低位的数加上较高位的数 (大雾)

异或

有点特殊? 有点像 FFT?那就记成 FFT 的形式就好了吧...

IFWT

IFWT 就是把 FWT 给还原一下就好了吧。

之前某位置加上了一个数,而现在那个数还没变 (大雾),那么就把那个数减掉就好了吧。

(对于异或)找不到之前那个数?没关系,可以列个方程解出来 (大雾)

注意!!

  • FWT不用倍长,不用蝴蝶变换

  • FWT里面还是尽量用<=比较安全,只要空间足够就没啥问题。特别注意一点:选取limit时要while (limi <= n)!!这时候一定要小于等于,否则不全!!

  • 其余各项同ntt里面的注意

补充说明:

对于或卷积:(\(FWT:f[] -> f'[]\))

\[f'[i]=\sum_{j|i=i}f[j] \]

发现 \(f'[i]\)\(i\) 的子集的权值和,因此可以做一些子集问题,如:P3175 [HAOI2015]按位或

子集卷积

P6097 【模板】子集卷积

给定\(a, b\)数组,令:

\[c_k = \sum_{i~and~j=0,i~or~j=k}a_i * b_j \]

即在 \(k\) 中找到两个互不相交的子集 \(i,j\),计算 \(a_i * b_j\) 的总和

如果不要求互不相交,那么就是个 FWT_OR 的板子题了。如果要求互不相交,那么只需加上限制 \(|i| + |j| = |k|\)

于是,我们可以枚举 \(|i|,|j|,|k|\),然后将所有大小为 \(|i|,|j|\)\(|a_i|,|b_j|\) 的总和乘一块加给 \(c_k\),然后再将 \(c\) 数组卷回去,即为答案。

例题:P6570 [NOI Online #3 提高组]优秀子序列(民间数据)(洛谷数据可以被暴力水过,随机数据下本机也可过,但是CCF的评测机太慢,过不去

应用

4589: Hard Nim

经典Nim游戏(轮流取石子,无法操作者为负)。

n堆石子满足每堆石子的初始数量是不超过m的质数。

求先手必败局面数。

n <= 1e9, m <= 5e4


最朴素的方法自然是枚举每一种方案,判断其合法不合法。

\[\sum_{i=1}^{m}\sum_{j=1}^{m}\sum_{k=1}^m...(n~sigma)...\sum_{l=1}^m[i,j,k,...,l=prime,i~xor~j~xor~k~xor~...~xor~l=0] \]

复杂度:O(\(nm^n\))

当只有两个sigma(n=2)的时候是:

\[\sum_{i=1}^m\sum_{j=1}^m[i,j=prime,i~xor~j=0] \]

\[\sum_{i~xor~j=0}{[i,j=prime]} \]

是异或卷积的形式。

(思维逐渐混乱)


根据FFT相关计数题目的经验,我们可以用权值数组。即 \(a[i]\) 表示异或值为 \(i\) 有多少种情况。然后就可以用FWT了。

考虑类似快速幂的方法,以快速幂的格式,把 \(a[~]\) 当作 \(x\),做多项式快速幂,最终答案就是 \(a[0]\)。复杂度大概\(O(m~logm~logn\))(有点悬?)

一想到多项式快速幂,我们就成功的走向了大弯路,甚至复杂度都有点保不住。不要忘记FFT,NTT,FWT都是借助点值表示加速的本质。在转化成点值表示以后,仍支持交换律,结合律等,因此可以直接把每个点值做快速幂。复杂度\(O(m~logm~+~m~logn)\)

\(Code:\)

limi = 1;
while (limi <= m)	limi <<= 1;
for (register int i = 0; i <= m; ++i)	A[i] = (!depri[i]);
FWT_xor(A, 1);
for (register int i = 0; i <= limi; ++i)	A[i] = quickpow(A[i], n);
FWT_xor(A, -1);
printf("%lld\n", A[0]);

CF662C Binary Table

先看题解 CF662C 【Binary Table】吧。

有一个 n 行 m 列的表格,每个元素都是 0/1 ,每次操作可以选择一行或一列,把 0/1 翻转,即把 0 换为 1 ,把 1 换为 0 。请问经过若干次操作后,表格中最少有多少个 1 。

\(n<=20, m<=1e5\)


考虑枚举行翻转情况为State。设一开始第 i 列的情况为 \(S_i\),则对于每个 \(State\) 来说,答案为(\(F_s\)表示某一列状态为 \(s\) 的最大贡献(1个数):

\[\sum_{i=1}^m{F_{State~xor~S_i}} \]

转换枚举对象: 枚举 对行操作后的列状态 X(方便直接使用 \(F_X\) 统计答案) 和 \(S_i\) (\(Q_s\) 表示状态为 \(S\) 的列有多少个):

\[\sum_{X=0}^{2^n}\sum_{S = 0}^{2^n}{[State~xor~S = X]F_{X}* Q_{S}} \]

稍作变换:

\[\sum_{X=0}^{2^n}\sum_{S = 0}^{2^n}{[State = X~xor~S]F_{X}* Q_{S}} \]

即:

\[\sum_{X~xor~S=State}{F_X * Q_S} \]

然后预处理出 F 和 Q ,FWT即可。

练习题

P3175 [HAOI2015]按位或

A国的贸易

FWT模板(调试用)

inline void FWT_or(ll *a, int type) {
	for (register int i = 1; i < limi; i <<= 1) {
		for (register int j = 0; j < limi; j += (i << 1)) {
			for (register int p = 0; p < i; ++p) {
				a[i + j + p] = (a[i + j + p] + a[j + p] * type) % P;
				if (a[i + j + p] < 0)	a[i + j + p] += P;
			}
		}
	}
}

inline void sol_or() {
	memcpy(tp1, A, sizeof(A));
	memcpy(tp2, B, sizeof(B));
	FWT_or(tp1, 1); FWT_or(tp2, 1);
	for (register int i = 0; i < limi; ++i)	C[i] = tp1[i] * tp2[i] % P;
	FWT_or(C, -1);
	for (register int i = 0; i < limi; ++i)	printf("%lld ", C[i]);
	puts("");
}
inline void FWT_and(ll *a, int type) {
	for (register int i = 1; i < limi; i <<= 1) {
		for (register int j = 0; j < limi; j += (i << 1)) {
			for (register int p = 0; p < i; ++p) {
				a[j + p] = (a[j + p] + a[i + j + p] * type) % P;
				if (a[j + p] < 0)	a[j + p] += P;
			}
		}
	}
}

inline void sol_and() {
	memcpy(tp1, A, sizeof(A));
	memcpy(tp2, B, sizeof(B));
	FWT_and(tp1, 1); FWT_and(tp2, 1);
	for (register int i = 0; i < limi; ++i)	C[i] = tp1[i] * tp2[i] % P;
	FWT_and(C, -1);
	for (register int i = 0; i < limi; ++i)	printf("%lld ", C[i]);
	puts("");
}
inline void FWT_xor(ll *a, int type) {
	for (register int i = 1; i < limi; i <<= 1) {
		for (register int j = 0; j < limi; j += (i << 1)) {
			for (register int p = 0; p < i; ++p) {
				ll nx = a[j + p], ny = a[i + j + p];
				a[j + p] = (nx + ny) % P;
				a[i + j + p] = (nx - ny + P) % P;
				if (type == -1) {
					a[j + p] = a[j + p] * inv2 % P;
					a[i + j + p] = a[i + j + p] * inv2 % P;
				}
			}
		}
	}
}

inline void sol_xor() {
	memcpy(tp1, A, sizeof(A));
	memcpy(tp2, B, sizeof(B));
	FWT_xor(tp1, 1); FWT_xor(tp2, 1);
	for (register int i = 0; i < limi; ++i)	C[i] = tp1[i] * tp2[i] % P;
	FWT_xor(C, -1);
	for (register int i = 0; i < limi; ++i)	printf("%lld ", C[i]);
	puts("");
}

猜你喜欢

转载自www.cnblogs.com/JiaZP/p/13398946.html
今日推荐