哈集幂大学习!

哈集幂,中立生物,常出没于省选与 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)\) 的关系:

\[\hat{h}(S) = \sum_{R \subseteq S} \sum_{A \cup B = R} f(A)g(B) \]

这相当于枚举所有满足 \(A \cup B \subseteq S\)\((A, B)\) 对,并累加 \(f(A)g(B)\)

\[\hat{f}(S) \cdot \hat{g}(S) = \left( \sum_{A \subseteq S} f(A) \right) \left( \sum_{B \subseteq S} g(B) \right) \]

这相当于枚举所有 \(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 的变换就是:

\[\begin{aligned} \hat{f}_{\text{0}} &= f_0 + f_1 \\ \hat{f}_{\text{1}} &= f_0 - f_1 \end{aligned} \]

(这里的加减是对应位置的分量进行)

这个操作对所有低位递归进行,类似于 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

咕咕咕!

posted @ 2026-03-08 17:57  Xuan_tmp  阅读(4)  评论(0)    收藏  举报