「学习笔记」FWT及集合幂级数入门

FWT

相比与 NTT/FFT 中的加法卷积,这里支持了位运算卷积,原理基于集合幂级数研究

vfk 的论文阅读没有任何门槛,所以就不抄一遍了

Code Display
inline void And(int *f,int lim,int opt){
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1; for(int k=0;k<lim;k+=p){
            for(int l=k;l<k+len;++l){
                if(opt==1) f[l]=add(f[l],f[l+len]); 
                else f[l]=del(f[l],f[l+len]);
            }
        }
    }return ;
}
inline void Or(int *f,int lim,int opt){
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1; for(int k=0;k<lim;k+=p){
            for(int l=k;l<k+len;++l){
                if(opt==1) f[l+len]=add(f[l+len],f[l]); 
                else f[l+len]=del(f[l+len],f[l]);
            }
        } 
    }return ;
}
inline void Xor(int *f,int lim,int opt){
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1; for(int k=0;k<lim;k+=p){
            for(int l=k;l<k+len;++l){
                f[l]=add(f[l],f[l+len]); 
                f[l+len]=del(f[l],add(f[l+len],f[l+len])); 
                if(opt==-1){
                    f[l]=mul(f[l],i2);
                    f[l+len]=mul(i2,f[l+len]);    
                }
            }
        }
    }return ;
}

子集卷积

这个和普通的 FWT 的不同之处是中间要对集合的大小相加

那么进行 FWT 之后中间对位数做加法卷积再 IFWT 即可

Code Display
struct node{
    int a[25];
    void operator+=(const node &p){
        for(int i=0;i<=n;++i) a[i]=add(a[i],p.a[i]); 
        return ;
    }
    void operator-=(const node &p){
        for(int i=0;i<=n;++i) a[i]=del(a[i],p.a[i]); 
        return ;
    }
    node operator*(const node &p)const{
        node ans; for(int i=0;i<25;++i) ans.a[i]=0; 
        for(int i=0;i<=n;++i) 
            for(int j=0;i+j<=n;++j)
                ans.a[i+j]=add(ans.a[i+j],mul(a[i],p.a[j])); 
        return ans;
    }
}F[N],G[N];
inline void FWT(node *f,int lim,int opt){
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1; 
        for(int k=0;k<lim;k+=p) 
            for(int l=k;l<k+len;++l) 
                if(opt==1) f[len+l]+=f[l]; 
                else f[l+len]-=f[l];
    } return ;
}
signed main(){
    n=read(); int lim=(1<<n); 
    for(int i=1;i<lim;++i) bit[i]=bit[i>>1]+(i&1); 
    for(int i=0;i<lim;++i) F[i].a[bit[i]]=read();
    for(int i=0;i<lim;++i) G[i].a[bit[i]]=read();
    FWT(F,lim,1); FWT(G,lim,1); 
    for(int i=0;i<lim;++i) F[i]=F[i]*G[i]; FWT(F,lim,-1);
    for(int i=0;i<lim;++i) printf("%lld ",F[i].a[bit[i]]); puts("");
    return 0;
}

集合幂级数

将形式幂级数中的 x 换成了 n0/1 向量 x ,具体建议阅读 2015 年 vfk 的论文

根据 FWT/FMT 的原理该运算支持除法操作,只要系数没有 0 且存在逆元即可,同时 FWT 本身也需要在满足运算存在对 2 逆元的局面下进行

而当不存在逆元的特殊情况可以看看 AGC034F 的做法,博主不清楚是不是有扩展性


求解 ln,exp 的手段比较抽象,先像子集卷积把元素分类,之后做 FMT

接下来变成了若干形式幂级数所以直接求,最后 IFMT 回去

因为不懂怎么分开 FMT 所以就鸽掉了

例题

THUPC2019 找树

其实 FWT 本质就是个求点值,转插值

先把题目转化为是判定每个 i 是否存在方案的计数问题,生成树问题启发我们使用矩阵树定理,位运算对应 FWT

那么对于每个加入的边,我们在 v 为第三维的数组中构造基尔霍夫矩阵,然后对每个 (x,y) 为第一第二维的数组做 FWT(因为边可能有重的)

考虑 DWT 就是把多项式转成点值,这里值得注意的是每个位上的运算就对应使用 And/OrFMT 或者 XorFWT

再反过来对每个权值求行列式的值,最后 IFWT 回去得到存在性

其实就是求点值和做插值

复杂度 Θ(n32w),代码不难写

实现细节:行列式求值的时候模个大点的质数,因为我们只关心存在性

这题启发我们 FWT 也可以转化成点插值来理解,对于不同的运算,转成点值了也就有了独立性

同时 FWT 完全可以理解成多个生成函数的卷积,这样其实也可以推得模板的正确性

SNOI2017 遗失的答案

分解 L,G 质因数和次幂

一个合法的方案必然满足每个数次幂的 min=Gp,最大的一个次幂是 Lp

观察到这个质因子个数很少,那么两位式状压:第一位表示是不是满足下界,第二位表示上界

这个状压很不套路,做前缀、后缀各一次 dp 得到信息

那么每个 L 的因数对其前后缀的信息做 AndFWT 取全集得到答案

实现的时候最后的每个因子要乘 gcd

Codeforces449D

题目本质是做一个背包然后求 f[0]

这个式子是个 And 卷积,只会 FWT,当然可以得到一个每次暴力卷的做法,但是复杂度很大

考虑这种 dp 的本质是某个物品如果有 i 个的话那么这个点的超集就会翻番(这和 FWT 的意思是一样的)

那么可以整体统计数之后 FWT 一次即可,在 IFWT 之前把每个 A[i] 替换成 2Ai

思考本质就能发现这个并不需要每次快速幂

统计答案记得减掉空集的情况,同时 FWT 的模数应为 1e9+6

uoj310 黎明前的巧克力

朴素的 dp 式子为 f[i][j]=f[i1][j]+2×f[i1][jai],使用 FWT 优化

但不一样的是这个题目中的系数变成了 1/3

那么对于 FWT 出来的数组,解一元一次方程得到 1 的个数 k3 的个数 nk(也就是必然会产生贡献)

那么用次幂替换掉之后 IFWT 即可

HAOI2015 按位或

需要 MinMax 容斥的科技:

min(S)=TS(1)|T|+1max(T)

同时可以反过来写:

max(S)=TS(1)|T|+1min(T)

证明主要考虑分 |T| 的奇偶性把多余的都消掉剩下 T={max(S)}

这题主要是考虑了应用到期望上的式子

E(max(S))=TS(1)|T|+1E(min(T))

最终求的就是 E(U) 那么现在没有的就是 E(min(T))

用封闭形式等等大力推推式子,得到 E(min(T))=11Sum(E(UT))

剩下的就是子集和,FWT 即可

PKUWC2018 随机游走

首先 minmax 容斥转化为求集合最小值

这部分推导确实是新做法:设成两者相关的一次函数

f(S,x)=(x,y)Ef(y)di+1

即求到了 S 集合中的点的最小步数,把父亲的提出来,设 f[t]=kt×f[x]+bt

推出来式子为 kx=1dxsumkx,bx=dx+sumbxdxsumkx,均与父亲节点的信息无关

那么对于每种取值集合都 dfs 一下得到 b[rt]

接下来就是 FWT 求子集和了

更多集合幂级数的题目参考 https://www.cnblogs.com/lhm-/p/14287558.html

posted @   没学完四大礼包不改名  阅读(450)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示