FWT小记


解决问题

\(c_i=\sum\limits_{j\circ k=i}a_j\times b_k\)

核心思想

对于 \(a,b\) 找到一个变换,使得可以将变换后的 \(a,b\) 直接点乘得到变换后的 \(c\) ,然后逆运算回来。

或卷积

\(f_i=\sum\limits_{j \ or\ i=i} a_j\)\(g_i=\sum\limits_{j \ or\ i=i} b_j\)\(h_i=f_i \times g_i\)

\(h_i=\sum\limits_{j\ or\ i = i} \ \sum\limits_{k\ or\ i=i} a_j\times b_k=\sum\limits_{(j\ or\ k)\ or \ i=i}a_j\times b_k\)

所以 \(h\) 就是 \(c\) 的变换后的数组。此时做一个高维差分即可。

与卷积

\(f_i=\sum\limits_{j \ and\ i=i} a_j\)\(g_i=\sum\limits_{j \ and\ i=i} b_j\)\(h_i=f_i \times g_i\)

\(h\) 做一个高位后缀差分。

异或卷积

定义 \(F(x)=popcount(x) \% 2\)

\(f_i=\sum\limits_{F(i\ xor j)=0}a_i-\sum\limits_{F(i\ xor j)=1}a_i\)\(g_i=\sum\limits_{F(i\ xor j)=0}a_i-\sum\limits_{F(i\ xor j)=1}b_i\)

可以暴力分类讨论得出 \(h_i=f_i \times g_i\)

实现方式

因为这类变换位之间都是独立的,我们考虑类似于 \(DP\) 那样从低到高一位一位地去实现。

void fwtor(ll *f,int op){
	for(ri len = 2,h = 1;len <= lim;len <<= 1,h <<= 1)
		for(ri i = 0;i < lim;i += len)
			for(ri j = i;j < i + h;++j)
				f[j + h] += f[j] * op,f[j + h] %= mod;
}
void fwtand(ll *f,int op){
	for(ri len = 2,h = 1;len <= lim;len <<= 1,h <<= 1)
		for(ri i = 0;i < lim;i += len)
			for(ri j = i;j < i + h;++j)
				f[j] += f[j + h] * op,f[j] %= mod;
}

void fwtxor(ll *f,int op){
	for(ri len = 2,h = 1;len <= lim;len <<= 1,h <<= 1)
		for(ri i = 0;i < lim;i += len)
			for(ri j = i;j < i + h;++j){
				f[j] = (f[j] + f[j+h]) % mod;
				f[j+h] = ((f[j] - f[j+h] - f[j+h]) % mod + mod) % mod;
				f[j] = f[j] * op % mod;
				f[j+h] = f[j+h] * op % mod;
			}
}

子集卷积

\(c_i=\sum\limits_{j\ or \ k=i\ ,\ j \ and\ k = i}a_j\times b_k\)

将条件转换,\(\sum\limits_{j\ or \ k=i\ ,\ F(j) + F(k) = F(j|k)}\)

\(F(i)\)\(a\) 分类 ,\(a'_{F(i),i} = a_i\) ,把 \(a',b'\) 求出 \(\text{fwt}\) ,然后手动卷那个 \(F(i)\) ,最后把 \(c'\)\(\text{fwt}\) 搞回 \(c'\)\(c_i=c'_{F(i),i}\)

//from 2022.2.3 11:40
#include
#define ri register int
#define ll long long
using namespace std;
const int maxn = (1<<20) + 5,mod = 1e9 + 9;
inline int rd(){
	int res = 0,f = 0; char ch = getchar();
	for(;!isdigit(ch);ch = getchar()) if(ch == '-') f = 1;
	for(;isdigit(ch);ch = getchar()) res = (res<<3) + (res<<1) + ch - 48;
	return f ? -res : res;
}
int n;
inline void fwt(ll *f,int op){//or 卷积
	for(ri mid = 1;mid < (1<>1] + (i&1);
	for(ri i = 0;i < (1<