算重学(4) FWT
https://www.cnblogs.com/alex-wei/p/set_power_series.html
https://www.luogu.com.cn/blog/command-block/wei-yun-suan-juan-ji-yu-ji-kuo-zhan
感觉好多人都有点误人子弟了。
高维前缀和
注意到求得是 \(f_S=\sum_{T\in S} g_T\),考虑压维直接做就好了。
当然,我们在做这个东西的时候,只是用了加法的结合律、交换律。因此,取 max,min 这些运算显然也可以做。
当然,如果需要差分还原回去的话,要求运算可逆。
https://www.luogu.com.cn/problem/AT_arc100_c
FWT
本质上是做一个线性变换。
记 \(c(i,j)\) 为 \(j\) 对 \(i\) 的贡献系数,然后一顿分析后只要构造出来的 \(c(i,j)\) 满足 \(c(i,j\otimes k)=c(i,j)c(i,k)\),即可。又因为位运算独立,因此你可以构造出来 \(c(i,j)=c'(i0,j0)c'(i1,j1)...\) 这样的形式,这样你只要满足等式在所有元为 \(0/1\) 时都成立即可。记 \(j\otimes k=p\),等式即为 \(c'(i0,p0)c'(i1,p1)...=c'(i0,j0)c'(i0,k0)c'(i1,j1)c'(i1,k1)...=c'(i0,j0\otimes k0)c'(i1,j1\otimes k1)...\),显然这个构造成立。
接下来,考虑分治,你注意到分治的过程的每一步都是乘上一个矩阵。
然后它这个实现的过程可以考虑当前最高位限制为 \(n\),看看现在序列 \(f\) 究竟存的是啥 \(f'(i)=\sum_{l\le j<r}c(i',j')f(j),i'为 i 保留前 n 位,j' 同理\),这个东西即为定义式。然后你考虑从上次做完的结构推出来,\(f'(i)=\sum_{l\le j<mid}c(i',j')f(j)+\sum_{mid\le j<r}c(i',j')f(j)\),然后把 \(n\) 恰出去,则你会得到 \(f_n(i)=c(i_n,0)f_{n-1}(i)+c(i_n,1)f_{n-1}(i+2^{n-1})\)。
其实本质上我们理应更强的限制一下计算式,设做规模为 \(2^n\) 的 FWT,\(f'(i)=\sum_{0\le j<2^n}c(i,j)f(j)\),那么显然你最高位为 \(n-1\),然后你把 \(n-1\) 提出去,发现规模缩小了一半,即两部都是 \(\sum_{0\le j<2^{n-1}}c(i',j')f(j)\),这样不难看出即为 \(2^{n-1}\) 规模的 FWT 做出来的答案。
然后 IFWT 的过程就是你在每一步变为乘上逆矩阵,但为啥你的方向没有变化!
我也想不明白,但是显然你回溯上去,每一步乘逆矩阵的写法一定是对的!而那些将 FWT,IFWT 整合在一起的,我感觉应该是用了结合律,交换律吧。但我还是推荐大家写开 2 种!
本质上应该都是在做一个线性变换,所以你不把它看成是逆的过程也是没事的,二者都是正确的。
然后边界问题的话,我们的过程是对原先的 \(f\) 做线性变换,自然边界也是 \(f\) 了。
或者理性点思考,你当最高位限制为 \(1\) 时,自然区间长度只有 \(2\),此时每个数只有 \(1\) 位,即第 \(0\) 位,左边第 \(0\) 位为 \(0\),右边 \(1\),然后你考虑定义式 \(f'_i=c(0,0)f_i+c(0,1)f_{i+1}\),自然边界也是这个东西了。
#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int mod=998244353,M=(int)(1e5),N=(1ll<<17)+5;
int fpow(int x,int y) {
int res=1; x%=mod;
while(y) {
if(y&1) res=res*x%mod; y>>=1; x=x*x%mod;
} return res;
}
const int inv2=fpow(2,mod-2);
const int
Cor[2][2]={{1,0},{1,1}},
Cand[2][2]={{1,1},{0,1}},
Cxor[2][2]={{1,1},{1,mod-1}},
ICor[2][2]={{1,0},{mod-1,1}},
ICand[2][2]={{1,mod-1},{0,1}},
ICxor[2][2]={{inv2,inv2},{inv2,mod-inv2}};
int n,a[N],b[N],f[N],g[N],pre;
void FWT(int *F,const int c[2][2]) {
for(int mx=1;mx<=pre;mx++) {
int len=(1ll<<mx);
for(int l=0;l<n;l+=len) {
for(int i=l;i<l+(len/2);i++) {
int x=F[i],y=F[i+(len/2)];
F[i]=(c[0][0]*x%mod+c[0][1]*y%mod)%mod;
F[i+(len/2)]=(c[1][0]*x%mod+c[1][1]*y%mod)%mod;
}
}
}
}
void IFWT(int *F,const int c[2][2]) {
for(int mx=pre;mx>=1;mx--) {
int len=(1ll<<mx);
for(int l=0;l<n;l+=len) {
for(int i=l;i<l+(len/2);i++) {
int x=F[i],y=F[i+(len/2)];
F[i]=(c[0][0]*x%mod+c[0][1]*y%mod)%mod;
F[i+(len/2)]=(c[1][0]*x%mod+c[1][1]*y%mod)%mod;
}
}
}
}
void RE() {
for(int i=0;i<n;i++) f[i]=a[i],g[i]=b[i];
}
void PR() {
for(int i=0;i<n;i++) cout<<(f[i]%mod+mod)%mod<<' ';
cout<<'\n';
}
signed main() {
cin.tie(0); ios::sync_with_stdio(false);
cin>>n; pre=n; n=(1ll<<n);
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++) cin>>b[i];
RE(); FWT(f,Cor); FWT(g,Cor);
for(int i=0;i<n;i++) f[i]=f[i]*g[i]%mod;
IFWT(f,ICor); PR();
RE(); FWT(f,Cand); FWT(g,Cand);
for(int i=0;i<n;i++) f[i]=f[i]*g[i]%mod;
IFWT(f,ICand); PR();
RE(); FWT(f,Cxor); FWT(g,Cxor);
for(int i=0;i<n;i++) f[i]=f[i]*g[i]%mod;
IFWT(f,ICxor); PR();
return 0;
}