集合幂级数
子集卷积
只有 \(i\cup j=k\) 的限制,我们已经会做了,难点在于 \(i\cap j=0\) 的限制。
发现 \(i\cap j=0\),相当于 \(|i|+|j|=|i\cup j|\)。那么可以再开一维记录 \(|S|\)。
设 \(f_{i,j}=[|j|=i]a_j\),\(g_{i,j}=[|j|=i]b_j\),那么 \(h_i=\sum_{k=0}^i f_k*g_{i-k}\) 就是答案(\(*\) 是点乘)。
\(c_i=h_{|i|,i}\)。
for (int i=0; i<21; i++){
fwt(f[i]),fwt(g[i]);
}
for (int i=0; i<21; i++){
for (int j=0; j<(1<<n); j++){
for (int k=0; k<=i; k++){
h[i][j]=(h[i][j]+f[k][j]*g[i-k][j]%mod)%mod;
}
}
ifwt(h[i]);
}
for (int i=0; i<(1<<n); i++){
cout<<h[ppc(i)][i]<<" ";
}
集合幂级数
域 \(F\) 上的集合幂级数 \(f\) 是 \(2^U\rightarrow F\) 的函数,平常表示为 \(f=\sum_{S\in 2^U} f_Sx^S\),\(x^S\) 作为一个占位符,其中 \(S\) 可以是集合的二进制表示。
看上去和形式幂级数很像。
加法:\(h=f+g\),则 \(h_S=f_S+g_S\)。
乘法:\(h=f\cdot g=\sum_{L\in 2^U}\sum_{R\in 2^U}f_Lx^L\cdot g_Lx^R\)。其中 \(x^L\cdot x^R=x^{L\oplus R}\),其中 \(\oplus\) 是位运算。其满足有分配律,交换律,结合律。
集合幂级数 \(\exp\)
同样的,设 \([x^Sy^i]F(x,y)=[x^S]F(x)\times [|S|=i]\),和子集卷积一样。
在 \(x\) 维上 \(\text{FWT}\),可以使不同的 \(S\) 之间独立,故而转为 \(g(y)=e^{f(y)}\) 的多项式 \(\exp\)。
求导可知 \(g'(y)=e^{f(y)}f'(y)=g(y)f'(y)\)。
所以 \([y^n]g'(y)=\sum_{i=0}^n [y^{n-i}]g(y)\times [y^i]f'(y)\)。熟知 \((x^{n+1})'=(n+1)x^n\),可以整理得到 \([y^n]g(y)=\frac{1}{n}\sum_{i=1}^n [y^{n-i}]g(y)\times i[y^i]f(y)\)。
(*)这个方法的本质是什么?求导可以使得多项式从 \([y^n]\) 变成 \((n+1)[y^{n+1}]\),相当于不会出现“自我贡献”,从而可以递推求解。求导是一种将次,也可以说是一种升次。
for (int i=0; i<21; i++){
fwt(f[i]);
}
for (int i=0; i<(1<<n); i++){
g[0][i]=1;
}
for (int i=1; i<21; i++){
ll iv=pw(i,mod-2);
for (int j=0; j<(1<<n); j++){
for (int k=1; k<=i; k++){
g[i][j]=(g[i][j]+g[i-k][j]*f[k][j]%mod*k)%mod;
}
g[i][j]=g[i][j]*iv%mod;
}
}
for (int i=0; i<21; i++){
ifwt(g[i]);
}
其实可以看出,这个和子集卷积的代码很相像。
集合幂级数 \(\exp\)(非素数模数)
题目中给定
可以发现他其实等于
那么 \([x^S]e^{F(x)}\) 相当于划分 \(S\) 的权值和,权值定义为 \([x^{S_k}]F(x)\) 的积,并且那么是无序的。组合意义可知必定满足 \([x^{\varnothing}]F(x)=0\)。
因为是无序的,所以钦定一个顺序:从大到小。设 \(S\) 最大元为 \(u\),枚举 \(u\in T\),那么
从小到大枚举 \(u\)(因为大的 \(u\) 由小的 \(u\) 递推而来)。发现把 \(u\) 去掉不管,剩下的做子集卷积,最后再位移 \(2^{u-1}\) 即可。
具体的,设 \(S\setminus \{u\}=S'\),\(T\setminus \{u\}=T'\),那么相当于 \([x^{T'}]F(x)\times [x^{S'\setminus T'}]e^{F(x)}\rightarrow [x^{S'}]e^{F(x)}\)。把 \(e^{F(x)}\) 看作 \(g\),一样的操作。
g[0][0]=1;
for (int u=1; u<=n; u++){
int cur=(1<<u-1);
for (int j=1; j<=u; j++){
fwt(f[j]+cur,u-1);
}
for (int j=0; j<u; j++){
fwt(g[j],u-1);
}
for (int i=1; i<=u; i++){
for (int j=0; j<=u-i; j++){
for (int k=0; k<(1<<u-1); k++){
g[i+j][k+cur]+=f[i][k+cur]*g[j][k];
}
}
}
for (int i=0; i<u; i++){
ifwt(g[i],u-1);
}
for (int i=1; i<=u; i++){
ifwt(g[i]+cur,u-1);
}
}
注意这里的 +cur 相当于位移,因为 fwt 传入的是一个指针。

浙公网安备 33010602011771号