zoukankan      html  css  js  c++  java
  • [学习笔记]FWT(快速沃尔什变换)

    写在前面

    本蒟蒻很菜,所以这篇博客依然几乎不会有证明

    还是先说这东西是干什么的

    平时我们所见的卷积是这样的:

    [h(k) = sum_{i + j = k} f(i) cdot g(j) ]

    但是偶尔还会遇到条件中的加号变成其它符号的情况,(FWT)就是这个符号是按位与,或,异或时快速求解(h)

    主要的三种情况

    其实就是我们要构造一种变换(tf),使得(tf(h) = tf(f) cdot tf(g)),其中点乘表示对应位相乘

    或卷积(|)

    即:

    [h(k) = sum_{i | j = k} f(i) cdot g(j) ]

    我们可以构造变换(FWT(A)),使得(FWT(A)_i = sum_{j | i = i} A_j)(我也不知道这个方法是怎么想出来的……)

    但是验证它还是不难的,首先有这个:(i | j | k = k Rightarrow i | k = k, j | k = k)

    那么

    [egin{align} FWT(h)_i = sum_{j | i = i} h_j & = sum_{j | i = i} sum_{s | t = j} f(s) cdot g(t) \ & = sum_{s | i = i} sum_{t | i = i} f(s) cdot g(t) \ & = sum_{s | i = i} f(s) sum_{t | i = i} g(t) = FWT(f)_i cdot FWT(g)_i end{align} ]

    然后问题变成了快速求(FWT(A))

    由于这个变换满足下面的等式:

    [FWT(A) = (FWT(A_0), FWT(A_0) + FWT(A_1)) ]

    其中(A_0)表示序列前半段,(A1)表示序列后半段,加号表示对应位置相加。简单理解一下就是根据定义,(FWT(A)_i)(i)的所有子集(j)(A_j)之和,而序列前半段的下标二进制最高位为(0),增加这一位并不会产生新的子集,后半段下标二进制最高位为(1),增加这一位后前面高位为(0)的部分也会成为新的子集

    所以显然就可以每次将序列分成前后两段分别计算,然后合并即可

    然后怎么逆变换呢,根据上面的正变换,不难发现就是减掉所有它的子集处的值就是了,可以得出:

    [IFWT(A) = (IFWT(A_0), IFWT(A_1) - IFWT(A_0)) ]

    与卷积(&)

    与和或在各个方面都联系紧密,那么就可以类比或卷积,不难得出:

    [egin{align} FWT(A) & = (FWT(A_0) + FWT(A_1), FWT(A_1)) \ IFWT(A) & = (IFWT(A_0) - IFWT(A_1), IFWT(A_1)) end{align} ]

    异或卷积(^)

    这个比较复杂,蒟蒻我并没能吃透,好像和奇偶性有关??

    结论是:

    [egin{align} FWT(A) & = (FWT(A_0) + FWT(A_1), FWT(A_0) - FWT(A_1)) \ IFWT(A) & = (frac{IFWT(A_0) + IFWT(A_1)}{2}, frac{IFWT(A_0) - IFWT(A_1)}{2}) end{align} ]

    另外一个很少见的运算——同或((odot))

    直接上结论了:

    [egin{align} FWT(A) & = (FWT(A_1) - FWT(A_0), FWT(A_1) + FWT(A_0)) \ IFWT(A) & = (frac{IFWT(A_1) - IFWT(A_0)}{2}, frac{IFWT(A_1) + IFWT(A_0)}{2}) end{align} ]

    代码

    模板题戳这里

    因为没见到同或的题,也没有看见过同或的模板,下面的代码只有前三种运算QwQ

    还有就是取模意义下除以(2)转换为乘上逆元QwQ

    void inc(LL &x, LL y) { x += y; if (x >= mod) x -= mod; }
    void dec(LL &x, LL y) { x -= y; if (x < 0) x += mod; }
    /*
    	type == 0  =>  or
    	type == 1  =>  and
    	type == 2  =>  xor
    */
    void FWT(LL *arr, int n, int type) {
    	for (int len = 2, half = 1; len <= (1 << n); len <<= 1, half <<= 1)
    		for (int i = 0; i < (1 << n); i += len)
    			for (int j = 0; j < half; ++j)
    				if (type == 0) inc(arr[i + half + j], arr[i + j]);
    				else if (type == 1) inc(arr[i + j], arr[i + half + j]);
    				else {
    					LL a0 = arr[i + j], a1 = arr[i + half + j];
    					inc(arr[i + j], a1); dec(arr[i + half + j] = a0, a1);
    				}
    }
    void IFWT(LL *arr, int n, int type) {
    	for (int len = 2, half = 1; len <= (1 << n); len <<= 1, half <<= 1)
    		for (int i = 0; i < (1 << n); i += len)
    			for (int j = 0; j < half; ++j)
    				if (type == 0) dec(arr[i + half + j], arr[i + j]);
    				else if (type == 1) dec(arr[i + j], arr[i + half + j]);
    				else {
    					LL a0 = arr[i + j], a1 = arr[i + half + j];
    					arr[i + j] = (a0 + a1) * inv % mod;
    					arr[i + half + j] = (a0 - a1) * inv % mod;
    					if (arr[i + half + j] < 0) arr[i + half + j] += mod;
    				}
    }
    
  • 相关阅读:
    asp.net core 使用 signalR(一)
    实现一个基于码云的Storage
    架构设计原则
    给 asp.net core 写个中间件来记录接口耗时
    [svc]ext4文件删除&访问原理
    [svc]为何linux ext4文件系统目录默认大小是4k?
    [svc]traceroute(udp+icmp)&tracert(icmp)原理
    [jk]服务器远控卡及kvm切换器
    [svc]find+xargs/sed&sed后向引用+awk多匹配符+过滤行绝招总结&&产生随机数
    [svc]linux正则及grep常用手法
  • 原文地址:https://www.cnblogs.com/Rhein-E/p/10502969.html
Copyright © 2011-2022 走看看