对卷积的一些理解与 FWT

UPD. 密码已去除

符号表示按位或(AND), 符号表示按位与(OR), 符号表示按位异或(XOR),¬ 表示按位取反(NOT).

话说序列到底用 a 表示好还是 {a} 表示好啊(

卷积还有一个理解是 https://gauss0320.blog.luogu.org/zong-xian-xing-bian-huan-di-jiao-du-kan-dai-fwt(Luogu4717 题解)

卷积 (Convolution)

数列卷积

卷积的定义:

两个序列 A,B 的卷积是

Cn=i=+AiBni

简记为 C=AB .

然而我们一般所说的序列下标都是不小于 0 的,于是

Cn=i=0nAiBni

这就是卷积的定义了 .

当然还可以写成这个样子

Cn=i+j=nAiBj


当然我们也可以定义一些别的卷积,类似这样:

Cn=ij=nAiBj

其中 是一个运算符 .

注意我们保留了乘积的形式 .

卷积定理

对于一个神奇的函数 傅里叶变换(Fourier Transform,FT),有

F(fg)=F(f)F(g)

这就是卷积定理,关于 FT 是啥就不说了,直接按 DFT 理解就好了 .

卷积定理对于其他的卷积也有时成立 .

卷积定理可以大大加速卷积的计算,十分重要 .

生成函数角度的理解

生成函数(Generating Function,GF)大家都知道吧,其实我们各种类型的生成函数都可以通过下面这种柿子统一:

F(z)=nankn(z)

其中 k 被成为 核函数 .

例如 kn(z)=zn 时就是 OGF,kn(z)=znn! 时就是 EGF,kn(z)=nz 时就是 DGF,kn(X)=Pr(X=n) 时就是 PGF,等等 .


GF 的乘积就可以看作卷积,序列和生成函数之间形成一个一一对应 .

例如 OGF 情况,我们有

F(z)G(z)=(nanzn)(nbnzn)=nmanbmzn+m=kmakmbmzk

最后一步是枚举 k=n+m .

这样我们就将 OGF 的乘积化成 OGF 形式了,提取系数,有

[zn]F(z)G(z)=manmbm

这不就是卷积嘛!

类似的,EGF 的乘积称作二项卷积,DGF 的卷积称作 Dirichlet 卷积,等等 .

位运算卷积与 FWT

位运算卷积,即

Cn=ij=nAiBj

其中 ,(即位运算).

其实这个位运算是可以规约到集合的


同普通卷积一样,我们要寻找一个函数 S,使得其满足卷积定理

S(fg)=S(f)S(g)

本质上来讲,是 https://www.luogu.com.cn/blog/wangrx/solution-p-4717 .

按位或 (FWT - OR)

显然有 ji=iki=ijki=i .

FWT({a})=ji=iaj .

则若在按位或卷积意义下有 c=ab,则

FWT({a})iFWT({b})i=(ji=iaj)(ji=ibj)=ji=iki=iajbk=(jk)i=iajbk=FWT({c})i

于是 FWT 就是我们要找的 S

现在考虑怎么求 FWTFWT1 .

Part I. FWTFWT

将原序列 {a} 按二进制最高位分类,令 {a0} 表示 {a} 下标最高位为 0 的,{a1} 表示 {a} 下标最高位为 1 的,则

F({a})=merge(F({a0}),F({a0})+F({a1}))

其中 F=FWTmerge 为序列拼接,+ 是对应位相加 .

于是可以分治,从而 FWT-OR 被 O(nlogn) 解决 .

Part II. IFWTFWT1

依然将 {a} 按二进制最高位分类,分法相同,由 Part I 知:

F({a})=merge(F({a0}),F({a0})+F({a1}))

其中 F=FWT,于是:

{a}=merge({a0},{a1}{a0})

也可以分治,从而 IFWT-OR 被 O(nlogn) 解决 .

按位与 (FWT - AND)

和 FWT-OR 类似,令 FWT({a})i=ji=iaj 即可 .

发现 ji=iaj=ji=jaj,所以 FWT-OR 和 FWT-AND 是本质相同的 .

按位异或 (FWT - XOR)

其实 FWT 是叫 Fast Walsh–Hadamard Transform,然而 Walsh–Hadamard 这种原本就只指 FWT - XOR 的 .

定义 xy=popcount(xy)mod2,其中 popcount(x) 表示 x 二进制中 1 的个数 .

引一个引理:

Lemma

popcount(xy)popcount(x)+popcount(y)(mod2)

证明可以参考 CF1615D 题解,可以直接考虑真值表 .

于是我们就有

i(jk)popcount(i(jk))=popcount((ik)(jk))popcount(ik)+popcount(jk)(ik)(jk)(mod2)

显而易见的,就有 i(jk)=(ik)(jk) .

有了这个性质就好做了,定义 FWT({a})i=(ij=0aj)(ij=1aj) 即可 .

FWT({a})iFWT({b})i=((ij=0aj)(ij=1aj))((ij=0bj)(ij=1bj))=(ij=0bj)(ik=0bk)(ij=0bj)(ik=1bk)(ij=1bj)(ik=0bk)+(ij=1bj)(ik=1bk)=(i(jk)=0ajbk)(i(jk)=1ajbk)=FWT({a}{b})i

于是就类似 FWT-OR 了,令 F=FWT,则

F({a})=merge(F({a0})+F({a1}),F({a0})F({a1}))

{a}=merge({a0}+{a1}2,{a0}{a1}2)

注意 IFWT-XOR 要乘 12 .

于是分治即可,FWT / IFWT - XOR 被 O(nlogn) 解决 .

从而位运算卷积(OR, AND, XOR)被 O(nlogn) 解决 .

代码

用 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;
}
posted @   yspm  阅读(53)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
😅​
点击右上角即可分享
微信分享提示