zoukankan      html  css  js  c++  java
  • FWT学习笔记

    快速沃尔什变换学习笔记

    (如果写错了请纠正)(表达不到位请多多包涵)


    (or)

    (f[i][x])表示第(i+1)位到第(n)位相同,第(1)位到第(i)位是(x)的子集的(a[y])的和

    于是FMT后的数组就是 (f[n][x])

    考虑如何计算(f[i][x])

    如果(x)的第(i)位是(0),那么(f[i][x]=f[i-1][x])

    如果是(1),那么(f[i][x]=f[i-1][x]+f[i-1][x-2^{i-1}])

    用滚动数组优化可以做到空间复杂度(O(n))

    对于第(i)层来说,相当于把整个序列分成了(2^{n-i})

    每一段中的第(i+1)位到第(n)位相同,且每段左半段第(i)位是(0),右半段第(i)位是(1),相当于左半段对右半段对应的位置产生了贡献

    代码就很容易写出来了(_)

    FMT的逆变换

    与正变换类似,(f[i][x])表示第(i+1)位到第(n)位是(x)的子集,且第(1)位到第(i)位相等的(a[y])的和

    如果(x)的第(i)位是(0),那么(f[i][x]=f[i-1][x])

    如果是(1),那么(f[i][x]=f[i-1][x]-f[i-1][x-2^{i-1}])

    是不是很简单(_)


    (and)

    (or)的本质相同


    (xor)

    这个就比较难了

    也叫集合的对称差卷积

    定义 (h=f cdot g)

    (h_S= sum_{L subseteq 2^U}sum_{R subseteq 2^U}[L oplus R=S]f_Lg_R)

    首先注意到对于集合 (S)

    (frac{1}{2^n}sum_{T subseteq 2^U} (-1)^{|S cap T|} = [ S= varnothing ])

    这样

    (h_S= sum_{L subseteq 2^U}sum_{R subseteq 2^U}[L oplus R oplus S = varnothing]f_L g_R)

    (=sum_{L subseteq 2^U}sum_{R subseteq 2^U} frac{1}{2^n} sum_{T subseteq 2^U}(-1)^{|T cap (L oplus R oplus S)|}f_Lg_R)

    (=sum_{L subseteq 2^U}sum_{R subseteq 2^U} frac{1}{2^n} sum_{T subseteq 2^U}(-1)^{|T cap L|}(-1)^{|T cap R|}(-1)^{|T cap S|}f_Lg_R)

    (=frac{1}{2^n}sum_{T subseteq 2^U} (-1)^{|T cap S |} left[ sum_{L subseteq 2^U} (-1)^{|T cap L|}f_L ight] left[ sum_{R subseteq 2^U} (-1)^{|T cap R|}g_R ight])

    如果我们做出如下定义

    对于集合幂级数 (f) 我们定义他的快速沃尔什变换为 (hat f)

    (hat {f_S} = sum_{T subseteq 2^U} f_T(-1)^{|S cap T|})

    那么 (hat{f})的逆变换 (f)

    (f_S =frac{1}{2^n} sum_{T subseteq 2^U} hat{f_S} (-1)^{|S cap T|})

    那么我们就有

    (h_S=frac{1}{2^n}sum_{T subseteq 2^U} hat{f_T} hat{g_T})

    所以就有 (hat{f_S}hat{g_S}=hat{h_S})

    这样就会得到(h_S=frac{1}{2^n}sum_{T subseteq 2^U} hat{h_S})

    所以现在的问题是怎么求 (f) 以及 (hat{f})

    同样还是 DP

    (f[i][x])表示第 (i+1) 到第 (n) 位相同,且对((-1))的指数贡献不考虑第 (i+1) 到第 (n)((-1)^k imes a[y]) 的和

    对于第 (i) 层来说

    如果第 (i) 位是 (0) , 那么 (f[i][x]=f[i-1][x]+f[i-1][x+2^{i-1}])

    如果第 (i) 位是 (1) , 那么 (f[i][x]=f[i-1][x-2^{i-1}]-f[i-1][x])

    对于第(i)层的每一段,令 (t1=f[i-1][x],t2=f[i-1][x+2^{i-1}])

    那么 (f[i][x]=t1+t2)

    (f[i][x+2^{i-1}]=t1-t2)

    对于逆变换则把它倒回去就可以了

    (f[i][x])表示 (1)(i)位相等,对指数贡献为 ((i+1))位到(n)位的((-1)^k imes a[y])的和

    如果第 (i) 位是 (0) , 那么 (f[i][x]=frac{f[i-1][x]+f[i-1][x+2^{i-1}]}{2})

    如果第 (i) 位是 (1) , 那么 (f[i][x]=frac{f[i-1][x-2^{i-1}]-f[i-1][x]}{2})


    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    const int mm=998244353;
    const int maxn=1000000;
    const int inv2=499122177;
    
    int n;
    long long A[maxn];
    long long B[maxn];
    long long C[maxn];
    
    void FMTor(long long *arr,int n,int f){
    	for(int k=1;k<n;k<<=1){
    		int p=k+k;
    		for(int i=0;i<n;i+=p){
    			for(int j=0;j<k;++j){
    				if(f==1){
    					arr[i+j+k]=(arr[i+j+k]+arr[i+j])%mm;
    				}else{
    					arr[i+j+k]=(arr[i+j+k]-arr[i+j]+mm)%mm;
    				}
    			}
    		}
    	}
    }
    void FMTand(long long *arr,int n,int f){
    	for(int k=1;k<n;k<<=1){
    		int p=k+k;
    		for(int i=0;i<n;i+=p){
    			for(int j=0;j<k;++j){
    				if(f==1){
    					arr[i+j]=(arr[i+j]+arr[i+j+k])%mm;
    				}else{
    					arr[i+j]=(arr[i+j]-arr[i+j+k]+mm)%mm;
    				}
    			}
    		}
    	}
    }
    
    void FWTxor(long long *arr,int n,int f){
    	for(int k=1;k<n;k<<=1){
    		int p=k+k;
    		for(int i=0;i<n;i+=p){
    			for(int j=0;j<k;++j){
    				long long x=arr[i+j],y=arr[i+j+k];
    				if(f==1){
    					arr[i+j]=(x+y)%mm;
    					arr[i+j+k]=(x-y+mm)%mm;
    				}else{
    					arr[i+j]=(x+y)*inv2%mm;
    					arr[i+j+k]=(x-y+mm)*inv2%mm;
    				}
    			}
    		}
    	}
    }
    
    int main(){
    	scanf("%d",&n);
    	for(int i=0;i<(1<<n);++i)scanf("%lld",&A[i]);
    	for(int i=0;i<(1<<n);++i)scanf("%lld",&B[i]);
    	
    	FMTor(A,1<<n,1);
    	FMTor(B,1<<n,1);
    	for(int i=0;i<(1<<n);++i)C[i]=A[i]*B[i]%mm;
    	FMTor(A,1<<n,-1);
    	FMTor(B,1<<n,-1);
    	FMTor(C,1<<n,-1);
    	for(int i=0;i<(1<<n);++i)printf("%lld ",C[i]);
    	printf("
    ");
    	
    	FMTand(A,1<<n,1);
    	FMTand(B,1<<n,1);
    	for(int i=0;i<(1<<n);++i)C[i]=A[i]*B[i]%mm;
    	FMTand(A,1<<n,-1);
    	FMTand(B,1<<n,-1);
    	FMTand(C,1<<n,-1);
    	for(int i=0;i<(1<<n);++i)printf("%lld ",C[i]);
    	printf("
    ");
    	
    	FWTxor(A,1<<n,1);
    	FWTxor(B,1<<n,1);
    	for(int i=0;i<(1<<n);++i)C[i]=A[i]*B[i]%mm;
    	FWTxor(A,1<<n,-1);
    	FWTxor(B,1<<n,-1);
    	FWTxor(C,1<<n,-1);
    	for(int i=0;i<(1<<n);++i)printf("%lld ",C[i]);
    	printf("
    ");
    	return 0;
    }
    
    
  • 相关阅读:
    [转]常用数字处理算法的Verilog实现
    [转]Linux必学的60个命令
    [转]4位超前进位加法器代码及原理
    [转]FPGA & Verilog开发经验若干
    [转]Verilog中parameter和define的区别
    [转]VIM标记 mark 详解
    关于获取服务的需求列表
    Office 2007 探索之路 Outlook
    利用反射建立单一调用的WebService
    寻宝记
  • 原文地址:https://www.cnblogs.com/zzyer/p/9285283.html
Copyright © 2011-2022 走看看