对卷积的一些理解与 FWT
UPD. 密码已去除
令 符号表示按位或(AND), 符号表示按位与(OR), 符号表示按位异或(XOR), 表示按位取反(NOT).
话说序列到底用 表示好还是 表示好啊(
卷积还有一个理解是 https://gauss0320.blog.luogu.org/zong-xian-xing-bian-huan-di-jiao-du-kan-dai-fwt(Luogu4717 题解)
卷积 (Convolution)
数列卷积
卷积的定义:
两个序列 的卷积是
简记为 .
然而我们一般所说的序列下标都是不小于 的,于是
这就是卷积的定义了 .
当然还可以写成这个样子
当然我们也可以定义一些别的卷积,类似这样:
其中 是一个运算符 .
注意我们保留了乘积的形式 .
卷积定理
对于一个神奇的函数 傅里叶变换(Fourier Transform,FT),有
这就是卷积定理,关于 FT 是啥就不说了,直接按 DFT 理解就好了 .
卷积定理对于其他的卷积也有时成立 .
卷积定理可以大大加速卷积的计算,十分重要 .
生成函数角度的理解
生成函数(Generating Function,GF)大家都知道吧,其实我们各种类型的生成函数都可以通过下面这种柿子统一:
其中 被成为 核函数 .
例如 时就是 OGF, 时就是 EGF, 时就是 DGF, 时就是 PGF,等等 .
GF 的乘积就可以看作卷积,序列和生成函数之间形成一个一一对应 .
例如 OGF 情况,我们有
最后一步是枚举 .
这样我们就将 OGF 的乘积化成 OGF 形式了,提取系数,有
这不就是卷积嘛!
类似的,EGF 的乘积称作二项卷积,DGF 的卷积称作 Dirichlet 卷积,等等 .
位运算卷积与 FWT
位运算卷积,即
其中 是 或 (即位运算).
其实这个位运算是可以规约到集合的
同普通卷积一样,我们要寻找一个函数 ,使得其满足卷积定理
本质上来讲,是 https://www.luogu.com.cn/blog/wangrx/solution-p-4717 .
按位或 (FWT - OR)
显然有 且 则 .
令 .
则若在按位或卷积意义下有 ,则
于是 就是我们要找的 !
现在考虑怎么求 及 .
Part I. FWT()
将原序列 按二进制最高位分类,令 表示 下标最高位为 的, 表示 下标最高位为 的,则
其中 , 为序列拼接, 是对应位相加 .
于是可以分治,从而 FWT-OR 被 解决 .
Part II. IFWT()
依然将 按二进制最高位分类,分法相同,由 Part I 知:
其中 ,于是:
也可以分治,从而 IFWT-OR 被 解决 .
按位与 (FWT - AND)
和 FWT-OR 类似,令 即可 .
发现 ,所以 FWT-OR 和 FWT-AND 是本质相同的 .
按位异或 (FWT - XOR)
其实 FWT 是叫 Fast Walsh–Hadamard Transform,然而 Walsh–Hadamard 这种原本就只指 FWT - XOR 的 .
定义 ,其中 表示 二进制中 的个数 .
引一个引理:
Lemma
证明可以参考 CF1615D 题解,可以直接考虑真值表 .
于是我们就有
显而易见的,就有 .
有了这个性质就好做了,定义 即可 .
则
于是就类似 FWT-OR 了,令 ,则
注意 IFWT-XOR 要乘 .
于是分治即可,FWT / IFWT - XOR 被 解决 .
从而位运算卷积(OR, AND, XOR)被 解决 .
代码
用 lambda 表达式精简代码 .
不要管 namespace 名 .
// since C++14
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = (1 << 17) + 233, P = 998244353, INV2 = 499122177;
int n;
vector<ll> a, b;
namespace FWTbitwise
{
#define args vi& A, int i, int j, int k, int mode
typedef vector<ll> vi;
auto OR = [](args){(A[i+j+k] += A[i+j] * mode) %= P;};
auto AND = [](args){(A[i+j] += A[i+j+k] * mode) %= P;};
auto XOR = [](args)
{
(A[i+j] += A[i+j+k]) %= P;
A[i+j+k] = (A[i+j] - A[i+j+k] * 2 % P) % P;
(A[i+j] *= mode) %= P; (A[i+j+k] *= mode) %= P;
};
#undef args
inline void FWT(vi& A, int mode, auto method)
{
int n = A.size();
for (int st = 2, k = 1; st <= n; st <<= 1, k <<= 1)
for (int i=0; i<n; i+=st)
for (int j=0; j<k; j++) method(A, i, j, k, mode);
}
inline void conv(vi& A, const vi& B){for (int i=0; i<A.size(); i++) (A[i] *= B[i]) %= P;}
}
int main()
{
using namespace FWTbitwise;
scanf("%d", &n); n = 1 << n;
for (int i=0, x; i<n; i++) scanf("%d", &x), a.emplace_back(x);
for (int i=0, x; i<n; i++) scanf("%d", &x), b.emplace_back(x);
vector<ll> tmpa(a), tmpb(b);
a = tmpa; b = tmpb; FWT(a, 1, OR); FWT(b, 1, OR); conv(a, b); FWT(a, P-1, OR);
for (int i=0; i<n; i++) printf("%lld ", (a[i] + P) % P); puts("");
a = tmpa; b = tmpb; FWT(a, 1, AND); FWT(b, 1, AND); conv(a, b); FWT(a, P-1, AND);
for (int i=0; i<n; i++) printf("%lld ", (a[i] + P) % P); puts("");
a = tmpa; b = tmpb; FWT(a, 1, XOR); FWT(b, 1, XOR); conv(a, b); FWT(a, INV2, XOR);
for (int i=0; i<n; i++) printf("%lld ", (a[i] + P) % P); puts("");
return 0;
}
以下是博客签名,正文无关
本文来自博客园,作者:yspm,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/16400958.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】