• FWT学习笔记


    引入

    现在有一个长成这样子的卷积:

    [C_i=sum_{joplus k=i}A_j imes B_k ]

    其中 (oplus) 可以是异或,与,非,序列长度都是 (2^n)

    解决

    这就需要 FWT 来解决了。

    我们之前做 FFT 的时候,是把多项式转化成点值的形式,然后就可以点值相乘了,那么我们也想在 FWT 的过程中实现这个东西,设 (A'[i]) 表示多项式 (A) 转化为点值之后的第 (i) 个点,那么就有:

    [C'[i]=A'[i] imes B'[i] ]

    我们再设 (c(i,j)) 表示多项式第 (j) 项的系数给第 (i) 个点的贡献,那么就有:

    [A'[i]=sum_{j=0}^{2^n-1}A_j ]

    [C'[i]=sum_{j=0}^{2^n-1}c(i,j)A_j imessum_{k=0}^{2^n-1}c(i,k)B_k=sum_{j=0}^{2^n-1}sum_{k=0}^{2^n-1}c(i,j)c(i,k)A_jB_k ]

    而又有:

    [C_p=sum_{t_1oplus t_2=p}A_{t_1} imes B_{t_2} ]

    [sum_{p=0}^{2^n-1}c(i,p)C_p=sum_{p=0}^{2^n-1}c(i,p)sum_{t_1oplus t_2=p}A_{t_1} imes B_{t_2} ]

    和上面的式子联立就有:

    [sum_{j=0}^{2^n-1}sum_{k=0}^{2^n-1}c(i,j)c(i,k)A_jB_k=sum_{p=0}^{2^n-1}c(i,p)sum_{t_1oplus t_2=p}A_{t_1} imes B_{t_2}=sum_{t_1=0}^{2^n-1}sum_{t_2=0}^{2^n-1}c(i,t_1oplus t_2)A_{t_1} imes B_{t_2} ]

    所以就有 (c(i,j)c(i,k)=c(i,joplus k)) ,这也是我们要求的 (c) 满足的条件。

    然后注意到位运算是可以按位拆开的,那么对于 (c) 也是如此,就有 (c(i,j)=prod_{k=0}^nc(i_k,j_k))

    然后考虑知道了 (c) 之后如何转化成点值形式,也就是计算下面这个式子:

    [A'[i]=sum_{j=0}^{2^n-1}c(i,j)A_j ]

    把这个折半,就有:

    [A'[i]=sum_{j=0}^{2^{n-1}-1}c(i,j)A_j+sum_{j=2^{n-1}}^{2^n-1}c(i,j)A_j ]

    把它的首位单独拿出来,写 (c(i,j)=c(i_0,j_0)c(i',j')) ,就有:

    [A'[i]=c(i_0,0)sum_{j=0}^{2^{n-1}-1}c(i',j')A_j+c(i_0,1)sum_{j=2^{n-1}}^{2^n-1}c(i',j')A_j ]

    这样子问题就减半了,可以分治下去。

    最后我们需要求 (c) ,这个就变得好求很多了,因为 (c(i,j)) 中的 (i,jin{0,1}) ,然后逆变换对矩阵求逆就可以了。

    那么我们写出这个矩阵

    [egin{bmatrix}c(0,0) & c(0,1)\c(1,0) & c(1,1)end{bmatrix} ]

    (or) 运算为例,我们可以得到关系式:

    [c(0,0)=c(0,0)c(0,0) ightarrow c(i,j)in{0,1} ]

    [c(0,1)=c(0,0)c(0,1),c(1,1)=c(1,0)c(1,1) ]

    通过上面的式子可以计算出好几种矩阵,但还需要满足矩阵有逆,那么得到其中一种矩阵:

    [c=egin{bmatrix}1 & 0\1 & 1end{bmatrix},c^{-1}=egin{bmatrix}1 & 0\-1 & 1end{bmatrix} ]

    同理,对于 (and) ,就有:

    [c=egin{bmatrix}0 & 1\1 & 1end{bmatrix},c^{-1}=egin{bmatrix}-1 & 1\1 & 0end{bmatrix} ]

    对于 (xor) ,就有:

    [c=egin{bmatrix}1 & -1\1 & 1end{bmatrix},c^{-1}=egin{bmatrix}frac{1}{2} & frac{1}{2}\-frac{1}{2} & frac{1}{2}end{bmatrix} ]

    这样子,我们也就可以在 (O(nlog n)) 的时间内解决这个问题了。

    例题

    1. 洛谷 P4717 【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT)

    就是引入的那道题。

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    const int N = 2e5;
    const int p = 998244353;
    using namespace std;
    int n,a[N + 5],b[N + 5],c[3][3],A[N + 5],B[N + 5];
    int mypow(int a,int x){int s = 1;for (;x;x & 1 ? s = 1ll * a * s % p : 0,a = 1ll * a * a % p,x >>= 1);return s;}
    void solve(int *a,int n)
    {
        for (int i = 1;i < (1 << n);i <<= 1)
            for (int j = 0;j < (1 << n);j += i << 1)
                for (int k = 0;k < i;k++)
                {
                    int a0 = a[j + k],a1 = a[j + k + i];
                    a[j + k] = (1ll * c[1][1] * a0 % p + 1ll * c[1][2] * a1 % p) % p;
                    a[j + k + i] = (1ll * c[2][1] * a0 % p + 1ll * c[2][2] * a1 % p) % p;
                }
    }
    void solveor()
    {
        c[1][2] = 0;
        c[1][1] = c[2][1] = c[2][2] = 1;
        for (int i = 0;i < (1 << n);i++)
            A[i] = a[i],B[i] = b[i];
        solve(A,n);
        solve(B,n);
        for (int i = 0;i < (1 << n);i++)
            A[i] = 1ll * A[i] * B[i] % p;
        c[1][1] = c[2][2] = 1;
        c[2][1] = -1;c[1][2] = 0;
        solve(A,n);
        for (int i = 0;i < (1 << n);i++)
            printf("%d ",(A[i] + p) % p);
        cout<<endl;
    }
    void solveand()
    {
        c[1][1] = 0;
        c[1][2] = c[2][1] = c[2][2] = 1;
        for (int i = 0;i < (1 << n);i++)
            A[i] = a[i],B[i] = b[i];
        solve(A,n);
        solve(B,n);
        for (int i = 0;i < (1 << n);i++)
            A[i] = 1ll * A[i] * B[i] % p;
        c[1][1] = -1;c[2][2] = 0;
        c[2][1] = c[1][2] = 1;    
        solve(A,n);
        for (int i = 0;i < (1 << n);i++)
            printf("%d ",(A[i] + p) % p);
        cout<<endl;
    }
    void solvexor()
    {
        c[1][2] = -1;
        c[2][1] = c[1][1] = c[2][2] = 1;
        for (int i = 0;i < (1 << n);i++)
            A[i] = a[i],B[i] = b[i];
        solve(A,n);
        solve(B,n);
        for (int i = 0;i < (1 << n);i++)
            A[i] = 1ll * A[i] * B[i] % p;
        c[1][1] = c[1][2] = c[2][2] = mypow(2,p - 2);
        c[2][1] = -mypow(2,p - 2);
        solve(A,n);
        for (int i = 0;i < (1 << n);i++)
            printf("%d ",(A[i] + p) % p);
        cout<<endl;
    }
    int main()
    {
        scanf("%d",&n);
        for (int i = 0;i < (1 << n);i++)
            scanf("%d",&a[i]);
        for (int i = 0;i < (1 << n);i++)
            scanf("%d",&b[i]);
        solveor();
        solveand();
        solvexor();
        return 0;
    }
    
    1. 洛谷 P3175 [HAOI2015]按位或
    1. CF449D Jzzhu and Numbers
    1. CF1119H Triple
  • 相关阅读:
    SQL Server 2008高可用性系列:日志传送 天高地厚
    SPICE(简单协议独立计算环境) 天高地厚
    (转载)RedHat桌面虚拟化介绍 天高地厚
    css让页面居中
    (转)AjaxPro实现机制探讨——Ajax是如何调用服务器端C#方法?
    Excel对象
    .net开发人员应该知道(一)
    SQLSERVER 动态执行SQL sp_executesql与EXEC
    存储过程中的 SET NOCOUNT ON
    .net开发人员应该知道(二)
  • 原文地址:https://www.cnblogs.com/sdlang/p/14292493.html
Copyright © 2020-2023  润新知