哈集幂大学习!
哈集幂,中立生物,常出没于省选与 NOI 中,哈集幂一般是无害的,但如果你没有认真学习它的生活习性而激怒了它,那么哈集幂很可能会将你创飞。
哈集幂,全称集合幂级数。其实就是取一个空间,空间中的每个元素是一个集合,在这上面做式子的运算。比如说我们有全集 \(U=\{1,2,\cdots,n\}\),子集 \(S\subseteq U\) 对应一个单项式 \(x^S\),一个哈集幂就是 \(\sum_{S\subseteq U}f(S) x^S\),\(f(S)\) 是实数或者整数(视题目决定)
我们考虑哈集幂和哈集幂的乘积,两个单项式的乘积应该是 \(x^S x^T=x^{S\odot T}\),但是 $\odot $ 到底是什么运算符取决于题目,一般是 $\cap,\cup,\oplus $ 中的一种。
\(\odot = \cup\) 与 \(\odot=\cap\)
我们先考虑 \(\odot=\cup\)。
此时两个哈集幂的乘积 \(\sum_{S\subseteq U}h(S) x^S=\sum_{A\subseteq U}f(A) x^A\sum_{B\subseteq U}g(B) x^B=\sum_{A,B\subseteq U}f(A)g(B)x^{A\cup B}\),因此有 \(h(S)=\sum_{A\cup B=S}f(A)g(B)\)。但是朴素的枚举子集还是太慢了!!
然后我们引入 FMT。我们设 \(\hat{f}(A)=\sum_{T\subseteq S}f(S)\),FMT 就是把 \(f\) 变为 $ \hat{f} $ 的过程,其实就是高维前缀和,每次枚举一个维度求和。如果是逆运算也就是高维差分,只需要把式子里的加(+=)变成减(-=)即可。
for(int j=0;j<n;j++)
for(int i=0;i<(1<<n);i++)
if(i>>j&1)f[i^(1<<j)]+=f[i];
那这坨东西看起来好像没什么用啊!别急,我们思考 \(\hat{h}(S)\) 和 \(\hat{f}(S)\hat{g}(S)\) 的关系:
这相当于枚举所有满足 \(A \cup B \subseteq S\) 的 \((A, B)\) 对,并累加 \(f(A)g(B)\)。
这相当于枚举所有 \(A \subseteq S\) 且 \(B \subseteq S\) 的对,并累加 \(f(A)g(B)\)。
而 \(A \subseteq S\wedge B \subseteq S\Leftrightarrow A \cup B \subseteq S\)。因为如果 \(A\) 和 \(B\) 都是 \(S\) 的子集,它们的并集当然也是 \(S\) 的子集;如果 \(A \cup B \subseteq S\),那么 \(A\) 和 \(B\) 各自当然也都是 \(S\) 的子集。
所以,左右两边枚举的 \((A, B)\) 对是完全相同的,于是 \(\hat{h}(S)= \hat{f}(S) \cdot \hat{g}(S)\)。此时直接朴素的对每一项做乘法即可得到 \(\hat{h}\),做 IFMT 即可得到 \(h\),可喜可贺。
对于 \(\odot=\cap\) 其实就是把高维前缀和换成高维后缀和,高维差分换成高维后缀差分(调换程序里 f[i] 和 f[i^(1<<j)] 的位置)。实在不行取 \(S'=U/S\) 就可以当成 \(\odot=\cup\) 理解了。
\(\odot=\oplus\)
此时我们需要引入 FWT。FWT 类似于 FFT,把整个集合按照二进制最高位分成两部分。
假设全集只有两个元素,对应二进制数 00、01、10、11。记最高位为 \(x\),低位为 \(y\),那么四个数可以看作:
- 最高位 0:\(00, 01\)(\(f_0\))
- 最高位 1:\(10, 11\)(\(f_1\))
FWT 的变换就是:
(这里的加减是对应位置的分量进行)
这个操作对所有低位递归进行,类似于 FFT 的蝴蝶变换把系数换成了 \(\pm 1\)。然后对变换得到的 \(\hat{f},\hat{g}\) 做分析可以得到 \(\hat{h}=\hat{f} \hat{g}\),但是怎么分析的有点不太明白??
for(int mid=2;mid<=n;mid<<=1)
for(int i=0,k=mid>>1;i<n;i+=mid)
for(int j=0;j<k;j++) {
int u=A[i+j],v=A[i+j+k];
A[i+j]=(u+v)%mod;
A[i+j+k]=(u-v+mod)%mod;
if(op!=1) {
A[i+j]=A[i+j]*inv2%mod;
A[i+j+k]=A[i+j+k]*inv2%mod;
}
}
子集卷积
形如 \(h(S)=\sum_{T\subseteq S}f(T)g(S/T)\),关键在于两个子集不重合。解法是先确定集合大小,如果 \(|A|+|B|=|S|\) 且 \(A\cup B=S\) 那么肯定有 $A\cap B=\emptyset $,然后对于每种大小做 OR 卷积就好了。
exp 和 ln
咕咕咕!

浙公网安备 33010602011771号