[学习笔记]FMT(快速莫比乌斯变换)&子集卷积(待填坑)

写在前面

由于本蒟蒻理解也不透彻,这篇博客只讲怎么做,没有证明 没有证明 没有证明

先说是拿来干嘛的

FMT是用来求解下面这种形式的“卷积”的:

[h(U) = sum_{S cup T = U} f(S) cdot g(T) ]

叫做“集合并卷积”

其中(U)(S)(T)是集合,用二进制数表示集合就是:

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

具体做法

类似(FFT)这种,(FMT)也是先将(f)(g)变换成点值表达,点值对应数乘后再变换回去就可以求得(h),也就是(FMT(h) = FMT(f) cdot FMT(g)),中间的点是数乘

于是有一种方案就是莫比乌斯变换,即(hat{f}(S) = sum_{T subseteq S} f(T))

它的逆变换就是(f(S) = sum_{T subseteq S} (-1)^{|S| - |T|} hat{f} (T)),证明可以容斥,具体……看这篇博客最上面……

那么就可以(O(3^n))暴力枚举子集来(逆)变换了

但是还有更快的方法

(O(n cdot 2^n))的快速变换

令:

[hat{f}_S^{(i)} = sum_{T subseteq S} [(S - T) subseteq { 1, 2, ..., i }]f_T ]

易得:

[hat{f}_S^{(0)} = f_S \ hat{f}_S^{(n)} = sum_{T subseteq S}[(S - T) subseteq { 1, 2, ..., n}]f_T = sum_{T subseteq S}f_T = hat{f} (S) ]

且对于不包含(i)(S),有递推式:

[hat{f}_S^{(i)} = hat{f}_S^{(i - 1)} ag{1} ]

[hat{f}_{S cup { i }}^{(i)} = hat{f}_S^{(i - 1)} + hat{f}_{S cup { i }}^{(i - 1)} ag{2} ]

简单理解一下就是:

((1))式:因为(S)不包含(i),所以满足条件的子集不会变

((2))式:前一部分为(S - T)不包含(i)的情况,后一部分为(S - T)一定包含(i)的情况

这样经过(n)轮递推,就可以求得(hat{f}_S^{(i)}),也就是(hat{f} (S))了,复杂度(O(n cdot 2^n))

代码

void FMT(double *a, int n) {
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < (1 << n); ++j)
			if (j & (1 << i)) a[j] += a[j ^ (1 << i)];
}

逆变换把+=换成-=就好了,不过如果是浮点运算,要注意精度问题

void IFMT(double *a, int n) {
	for (int i = 0; i < n; ++i)
		for (int j = 0; j < (1 << n); ++j)
			if (j & (1 << i)) a[j] -= a[j ^ (1 << i)];
}

例题

如果需要,移步[HAOI2015]按位或

挖坑:集合卷积

大概很快就会填坑吧……

原文地址:https://www.cnblogs.com/Rhein-E/p/10498378.html