浅谈FWT

FWT

背景

类似与\(FFT\),\(FWT\)是在下标位运算为背景的变换

\(FFT\)是利用单位根插值作为变换,而\(FWT\)同样要构造\(FWT(A),IFWT(A)\)

当然\(FWT(A\times B)=FWT(A)\ op \ FWT(B)\)

定义\(C=A\times B=\sum\limits_{i=j|k}A_jB_k\)

考虑构造\(FWT(A)_i=\sum\limits_{i=i|j}A_j\),相当于枚举\(i\)\(1\)的子集

\(FWT(C)_i=FWT(A)_iFWT(B)_i=\sum\limits_{i=i|j,i=i|k}A_jB_k=\sum\limits_{i=i|{p,p=j|k}}A_jB_k=\sum\limits_{i=i|p}C_p\)

现在问题在于如何求解\(FWT(A)\)

考虑将\(A\)按下标平分为\(A_0,A_1\),当然这里要补全到\(2\)的幂次

我们分别求解\(A_0,A_1\)在他们自己的长度下的\(FWT\)

对于\(A_0\),相对于\(A_1\)它的最高为\(0\),很明显不用考虑这一位,即他的贡献为\(FWT(A_0)\)

对于\(A_1\),最高位为\(1\),它不仅要考虑自己,也要考虑\(A_0\)的贡献即\(FWT(A_0)+FWT(A_1)\)

总上,\(FWT(A)=Merge(FWT(A_0),FWT(A_0)+FWT(A_1))\)

平分\(FWT(A)\)\(B_0,B_1\)

至于\(IFWT\),\(A_0=IFWT(B_0)\),\(A_1=IFWT(FWT(A_1))=IFWT(B_1-FWT(A_0))=IFWT(B_1)-IFWT(B_0)\)

\(IFWT(A)=Merge(IFWT(A_0),IFWT(A_1)-IFWT(A_0))\)

和或差不多

设计\(FWT(A)_i=\sum\limits_{i=i\&j}A_j\),相当于\(1\)的超集

\(A_0\)可以累加\(A_0,A_1\)的贡献,\(A_1\)只有他自己的

\(FWT(A)=Merge(FWT(A_0)+FWT(A_1),FWT(A_1))\)

\(IFWT(A)=Merage(IFWT(A_0)-IFWT(A_1),IFWT(A_1))\)

异或

这个构造有点奇怪

\(c(x)\)\(x\)\(1\)个数

\(FWT(A)_i=\sum\limits_{j}(-1)^{c(i\&j)}A_j\)

若有个性质\((c(i\&k)\&1)\oplus(c(j\&k)\&1)=(c((i\oplus j)\&k)\&1)\)

具体证明不会

然后考虑\(A\)的前部分,很明显\(A_0,A_1\)的结果是一样的

考虑后半部分,\(A_1\)\(1\)的个数会增加,则其变负

\(FWT(A)=Merge(FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_1))\)

\(IFWT\)类似与或

\(IFWT(A)=Merge(\dfrac{IFWT(A_0)+IFWT(A_1)}{2},\dfrac{IFWT(A_0)-IFWT(A_1)}{2})\)

#include<bits/stdc++.h>
using namespace std;
const int MOD=998244353;
struct Poly{
    vector<int>V;
    void FWT_OR(int Lit,int Type)
    {
        if(Type==1)
        {
            int len=(1<<Lit);
              for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        V[j]=V[j];
                        V[j+(Len/2)]=((long long)V[j]+V[j+(Len/2)])%MOD;
                    }
                }
            }
        }
        else
        {
            int len=(1<<Lit);
              for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        V[j]=V[j];
                        V[j+(Len/2)]=((long long)V[j+(Len/2)]-V[j]+MOD)%MOD;
                    }
                }
            }
        }
        
    }
    void FWT_AND(int Lit,int Type)
    {
        if(Type==1)
        {
            int len=(1<<Lit);
            for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        V[j]=((long long)V[j]+V[j+(Len/2)])%MOD;
                    }
                }
            }
        }
        else
        {
            int len=(1<<Lit);
            for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        V[j]=((long long)V[j]-V[j+(Len/2)]+MOD)%MOD;
                    }
                }
            }
        }
        
    }
    void FWT_XOR(int Lit,int Type)
    {
        int inv2=(MOD-MOD/2);
        if(Type==1)
        {
            int len=(1<<Lit);
            for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        int x=V[j];
                        int y=V[j+(Len/2)];
                        V[j]=((long long)x+y)%MOD;
                        V[j+(Len/2)]=((long long)x-y+MOD)%MOD;
                    }
                }
            }
        }
        else
        {
            int len=(1<<Lit);
            for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        int x=V[j];
                        int y=V[j+(Len/2)];
                        V[j]=((long long)x+y)%MOD;
                        V[j+(Len/2)]=((long long)x-y+MOD)%MOD;
                        V[j]=((long long)V[j]*inv2)%MOD;
                        V[j+(Len/2)]=((long long)V[j+(Len/2)]*inv2)%MOD;
                    }
                }
            }
        }
        
    }
};
Poly Mul_OR(Poly A,Poly B)
{
    int Lit=1;
    int N=max(A.V.size(),B.V.size());
    int now=0;
    while(Lit<N)
    {
        Lit*=2;
        now++;
    }
    while(A.V.size()<Lit)
    {
        A.V.push_back(0);
     }
     while(B.V.size()<Lit)
    {
        B.V.push_back(0);
     }
     A.FWT_OR(now,1);
     B.FWT_OR(now,1);
     Poly C;
     C.V.clear();
     for(int i=0;i<Lit;i++)
     {
        C.V.push_back(((long long)A.V[i]*B.V[i])%MOD);
     }
     C.FWT_OR(now,-1);
     return C;
}
Poly Mul_AND(Poly A,Poly B)
{
    int Lit=1;
    int N=max(A.V.size(),B.V.size());
    int now=0;
    while(Lit<N)
    {
        Lit*=2;
        now++;
    }
    while(A.V.size()<Lit)
    {
        A.V.push_back(0);
     }
     while(B.V.size()<Lit)
    {
        B.V.push_back(0);
     }
     A.FWT_AND(now,1);
     B.FWT_AND(now,1);
     Poly C;
     C.V.clear();
     for(int i=0;i<Lit;i++)
     {
        C.V.push_back(((long long)A.V[i]*B.V[i])%MOD);
     }
     C.FWT_AND(now,-1);
     return C;
}
Poly Mul_XOR(Poly A,Poly B)
{
    int Lit=1;
    int N=max(A.V.size(),B.V.size());
    int now=0;
    while(Lit<N)
    {
        Lit*=2;
        now++;
    }
    while(A.V.size()<Lit)
    {
        A.V.push_back(0);
     }
     while(B.V.size()<Lit)
    {
        B.V.push_back(0);
     }
     A.FWT_XOR(now,1);
     B.FWT_XOR(now,1);
     Poly C;
     C.V.clear();
     for(int i=0;i<Lit;i++)
     {
        C.V.push_back(((long long)A.V[i]*B.V[i])%MOD);
     }
     C.FWT_XOR(now,-1);
     return C;
}
Poly A,B;
int n;
int x;
int main()
{
  //  freopen("date.in","r",stdin);
    scanf("%d",&n);
    for(int i=0;i<(1<<n);i++)
    {
        scanf("%d",&x);
        A.V.push_back(x);
    }
     for(int i=0;i<(1<<n);i++)
    {
        scanf("%d",&x);
        B.V.push_back(x);
    }
    Poly C=Mul_OR(A,B);
    for(int i=0;i<(1<<n);i++)
    {
        printf("%d ",C.V[i]);
    }
    printf("\n");
    C=Mul_AND(A,B);
    for(int i=0;i<(1<<n);i++)
    {
        printf("%d ",C.V[i]);
    }
    printf("\n");
     C=Mul_XOR(A,B);
    for(int i=0;i<(1<<n);i++)
    {
        printf("%d ",C.V[i]);
    }
    printf("\n");
    return 0;
}

子集卷积

\(C_i=\sum\limits_{i=j\|k,(j\&k=0)}A_jB_k\)

注意到\(j|k\)的形式是\(FWT\)\(OR\),而\(j\&k=0\Rightarrow|j|+|k|=|j|k|\)

我们对子集的大小分类,\(A_{p,j}\)为子集为\(j\),大小为\(p\)

直接对\(A_{p}FWT\),然后与\(B\)卷积一下就可以了

#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+9;
struct Poly{
    vector<int>V;
    void FWT_OR(int Lit,int Type)
    {
        if(Type==1)
        {
            int len=(1<<Lit);
              for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        V[j]=V[j];
                        V[j+(Len/2)]=((long long)V[j]+V[j+(Len/2)])%MOD;
                    }
                }
            }
        }
        else
        {
            int len=(1<<Lit);
              for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        V[j]=V[j];
                        V[j+(Len/2)]=((long long)V[j+(Len/2)]-V[j]+MOD)%MOD;
                    }
                }
            }
        }
        
    }
    void FWT_AND(int Lit,int Type)
    {
        if(Type==1)
        {
            int len=(1<<Lit);
            for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        V[j]=((long long)V[j]+V[j+(Len/2)])%MOD;
                    }
                }
            }
        }
        else
        {
            int len=(1<<Lit);
            for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        V[j]=((long long)V[j]-V[j+(Len/2)]+MOD)%MOD;
                    }
                }
            }
        }
        
    }
    void FWT_XOR(int Lit,int Type)
    {
        int inv2=(MOD-MOD/2);
        if(Type==1)
        {
            int len=(1<<Lit);
            for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        int x=V[j];
                        int y=V[j+(Len/2)];
                        V[j]=((long long)x+y)%MOD;
                        V[j+(Len/2)]=((long long)x-y+MOD)%MOD;
                    }
                }
            }
        }
        else
        {
            int len=(1<<Lit);
            for(int Len=2;Len<=len;Len<<=1)
            {
                for(int i=0;i<len;i+=Len)
                {
                    for(int j=i;j<=(i+Len/2)-1;j++)
                    {
                        int x=V[j];
                        int y=V[j+(Len/2)];
                        V[j]=((long long)x+y)%MOD;
                        V[j+(Len/2)]=((long long)x-y+MOD)%MOD;
                        V[j]=((long long)V[j]*inv2)%MOD;
                        V[j+(Len/2)]=((long long)V[j+(Len/2)]*inv2)%MOD;
                    }
                }
            }
        }
        
    }
};
Poly Mul_OR(Poly A,Poly B)
{
    int Lit=1;
    int N=max(A.V.size(),B.V.size());
    int now=0;
    while(Lit<N)
    {
        Lit*=2;
        now++;
    }
    while(A.V.size()<Lit)
    {
        A.V.push_back(0);
     }
     while(B.V.size()<Lit)
    {
        B.V.push_back(0);
     }
     A.FWT_OR(now,1);
     B.FWT_OR(now,1);
     Poly C;
     C.V.clear();
     for(int i=0;i<Lit;i++)
     {
        C.V.push_back(((long long)A.V[i]*B.V[i])%MOD);
     }
     C.FWT_OR(now,-1);
     return C;
}
Poly Mul_AND(Poly A,Poly B)
{
    int Lit=1;
    int N=max(A.V.size(),B.V.size());
    int now=0;
    while(Lit<N)
    {
        Lit*=2;
        now++;
    }
    while(A.V.size()<Lit)
    {
        A.V.push_back(0);
     }
     while(B.V.size()<Lit)
    {
        B.V.push_back(0);
     }
     A.FWT_AND(now,1);
     B.FWT_AND(now,1);
     Poly C;
     C.V.clear();
     for(int i=0;i<Lit;i++)
     {
        C.V.push_back(((long long)A.V[i]*B.V[i])%MOD);
     }
     C.FWT_AND(now,-1);
     return C;
}
Poly Mul_XOR(Poly A,Poly B)
{
    int Lit=1;
    int N=max(A.V.size(),B.V.size());
    int now=0;
    while(Lit<N)
    {
        Lit*=2;
        now++;
    }
    while(A.V.size()<Lit)
    {
        A.V.push_back(0);
     }
     while(B.V.size()<Lit)
    {
        B.V.push_back(0);
     }
     A.FWT_XOR(now,1);
     B.FWT_XOR(now,1);
     Poly C;
     C.V.clear();
     for(int i=0;i<Lit;i++)
     {
        C.V.push_back(((long long)A.V[i]*B.V[i])%MOD);
     }
     C.FWT_XOR(now,-1);
     return C;
}
Poly A[21],B[21],H[21];
int n;
int x;
int main()
{
 //   freopen("date.in","r",stdin);
 //   freopen("date.out","w",stdout);
    scanf("%d",&n);
    for(int i=0;i<=n;i++)
    {
        A[i].V.resize((1<<n),0);
        B[i].V.resize((1<<n),0);
    }
    for(int i=0;i<(1<<n);i++)
    {
        scanf("%d",&x);
        A[__builtin_popcount(i)].V[i]=x;
    }
    for(int i=0;i<(1<<n);i++)
    {
        scanf("%d",&x);
        B[__builtin_popcount(i)].V[i]=x;
    }
    for(int i=0;i<=n;i++)
    {
        A[i].FWT_OR(n,1);
        B[i].FWT_OR(n,1);
    }
    for(int i=0;i<=n;i++)
    {
        H[i].V.resize((1<<n),0);
    }
    for(int S=0;S<(1<<n);S++)
    {
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=i;j++)
            {
                H[i].V[S]=((long long)H[i].V[S]+((long long)A[j].V[S]*B[i-j].V[S])%MOD)%MOD;
            }
        }
    }
    for(int i=0;i<=n;i++)
    {
        H[i].FWT_OR(n,-1);
    }
    for(int i=0;i<(1<<n);i++)
    {
        printf("%d ",H[__builtin_popcount(i)].V[i]);
    }

    return 0;
}
posted @ 2023-03-25 10:32  kid_magic  阅读(17)  评论(0编辑  收藏  举报