FWT 学习笔记&做题记录
前置知识
先来个模板:
FWT 用来解决像下面这样的卷积:
其中 是三个位运算中的一个。
FWT 的时间复杂度为 ,如果把 看成 ,那么时间复杂度和 FFT,NTT 相同。
与卷积
定义 表示 ,这是一个 FWT 变换,显然这个变换可以直接高维前缀和。
void FMT(int *A,int *ans){
for(int i=0;i<S;i++)ans[i]=A[i];
for(int i=0;i<n;i++)
for(int j=S-1;j>=0;j--)
if(j&(1<<i))ans[j^(1<<i)]+=ans[j];
}
然后会发现一个惊人的式子:
证明很简单,考虑后面的式子实际上就是
然后会发现 ,所以这样计算出来的显然和右边相等。
然后考虑咋从 变成 。
实际上就是再减回去,把加号变成减号即可。
void IFMT(int *A,int *ans){
for(int i=0;i<S;i++)ans[i]=A[i];
for(int i=0;i<n;i++)
for(int j=S-1;j>=0;j--)
if(j&(1<<i))ans[j^(1<<i)]-=ans[j];
}
然后你就会发现,两个的区别只有一个符号,可以直接带一个 进去。
卷积写法和 FFT 差不多,就是:
FWT(A,A,1),FWT(B,B,1);
for(int i=0;i<S;i++)ans[i]=A[i]*B[i];
FWT(ans,ans,-1);
和卷积就好了。
或卷积
和上面的差不多,可以发现本质上就是枚举顺序换一下就好了。
关于写法
FWT 写法可以和 FFT 长得一样,这样看上去更加优美。
//和卷积
void FMT_And(int *A,int T){
for(int len=1;len<1<<n;len<<=1)
for(int l=0;l<1<<n;l+=(len<<1))
for(int k=0;k<len;k++)
A[l+k]=A[l+k]+A[l+k+len]*T;
}
//或卷积
void FMT_Or(int *A,int T){
for(int len=1;len<1<<n;len<<=1)
for(int l=0;l<1<<n;l+=(len<<1))
for(int k=0;k<len;k++)
A[l+k+len]=A[l+k]*T+A[l+k+len];
}
可以发现本质上 在枚举哪一位, 在枚举这一位前面的, 是后面的,然后就和上面的写法一样了。
这样写的好处在于,无论什么运算,只需要在两个数的情况下满足,就可以直接写在里面。
异或卷积
最后讲是因为实在太难了。
神仙构造:。
令 表示 二进制位上 的个数,则上面 表示 。
可以发现 。
证明很简单,按位考虑,如果 这位是 ,那么左右都是 ,如果 是 ,那么 如果有 就会产生 的贡献,又因为有对 取模,所以左右相等。
考虑咋实现,对于两个数差一个二进制位的数来说,实际上就是前面的加上后面的,后面的减去前面的。
逆变换也很简单,直接还原即可。
void FMT_Xor(int *A,int T){
for(int len=1;len<1<<n;len<<=1)
for(int l=0;l<1<<n;l+=(len<<1))
for(int k=0;k<len;k++){
int x=A[l+k],y=A[l+k+len];
A[l+k]=x+y,A[l+k+len]=x-y;
if(T==-1)A[l+k]=A[l+k]/2,A[l+k+len]=A[l+k+len]/2;
}
}
做题记录
给长度为 的两个序列 ,求其子集卷积结果。
。
一个模板题。
考虑 的限制比较阴间,没有这个限制的话可以直接或卷积。
令 表示 二进制位上 的个数,那么可以发现,,仅当 时 。
那么有一个想法就是加一维限制,限制其位数,对于每一个或卷积之后在进行一个多项式卷积。
即
然后对每一位进行还原即可,时间复杂度 可过。
CF662C *2600
tourist 场上没做出的题被我做出了。
有一个 行 列的表格,每个元素都是 ,每次操作可以选择一行或一列,把 翻转,即把 换为 ,把 换为 。请问经过若干次操作后,表格中最少有多少个 。
。
这么小,显然先把每一列状压一下,假设状压结果是 。
枚举当前行上的操作 ,令 表示 ,即通过翻转得到的最小 数量。
令 , 表示 的出现次数。
显然异或卷积,直接卷就好了。
题比较长,自己看吧
首先,判断一个点集是否合法可以直接暴力,只需要满足不连通或者存在奇点即可,这部分可以直接 。
直接考虑当前选了多少个点,令 表示已经选了 个点,集合为 的乘积,这明显是一个子集卷积,枚举上一轮选了多少个点,直接和上面预处理的结果卷起来就好了。
每一轮之后再去除以整个集合的和,也就是式子的下半部分。
这样一直做 轮就做完了,时间复杂度 。
有 个数 , 次询问,每次询问给出 个质数,可以在 个数中选择若干个数,使得 个质数都能整除选出得数的乘积,求方案数对 。
。
发现 ,容易想到根号分治,然后可以发现 的质数只有 个,可以状压。
先不考虑 的,可以发现这本质上就是个或卷积,设 表示选了集合为 的因数的方案数,答案相当于把 个数每个数的 数组卷起来。
如何快速卷?把每个数组都 FWT,可以发现本质是一个其他都是 ,包含 的数是 的东西,所以可以全部加起来,然后 FWT 之后求一个 的幂次。
最后的答案相当于 IFWT 之后求一个后缀和。
然后考虑 的因数咋做,考虑分组,每个数分到自己 的因数的组里去,最后相当于每个组卷起来,如果一个组被选中,那么就相当于去掉一个都不选的方案,也就是减一。
时间复杂度 ,在民间数据中跑过了。
upd:棺方数据挂了,需要使用常数优化。
由于 ,所以 也可以分到后面,这样时间复杂度变成 。
注意需要特判 ,因为没有一个 的因数可以除掉它。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)