经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
浅谈集合幂级数
来源:cnblogs  作者:Xun_Xiaoyao  时间:2024/2/19 9:20:22  对本文有异议

叠甲:读者很菜。

集合幂级数是一个很厉害的东西。

我们对于是有限集的全集 \(\mathbb{U}={1,2,\dots n}\),我们利用占位符 \(x^S\) 来表示一个序列 \(f\),其中对于 \(S\subseteq \mathbb{U}\) 的值为 \(f_S\)

一般记为 \(F=\sum\limits_{S\subseteq \mathbb{U}}f_Sx^S\)

对于占位符的运算,有 \(x^S\times x^T=\begin{cases}x^{S\cup T}&,S\cap T=\varnothing\\0&,S\cap T\neq \varnothing\end{cases}\)

子集卷积

我们考虑最基本的卷积运算:

已知 \(F=\sum\limits_{S\subseteq \mathbb{U}}f_Sx^S\)\(G=\sum\limits_{S\subseteq \mathbb{U}}g_Sx^S\) 如何求解 \(H=F\times G\)

【模板】子集卷积

如果运算有 \(x^S\times x^T=x^{S\cup T}\),这就是普通的或卷积,可以 \(O(n2^n)\) 实现。

但是并不是,只有在 \(S\cap T=\varnothing\) 的时候才会做贡献,考虑在或卷积的基础上增加一些修改,使得其满足这个条件。

我们知道 \(|S|+|T|\ge |S\cup T|\),取等的时候当且仅当 \(S\cap T=\varnothing\),这就完美的满足了我们的条件。

所以我们添加一个占位符 \(y\)\(x^S\) 变成 \(x^Sy^|S|\),则 \((x^Sy^{|S|})\times (x^Ty^{|T|})=x^{S\cup T}y^{|S|+|T|}\),这样就完美的符合了我们的要求。

具体的实现,就是增加以为,这一位的运算是朴素的多项式卷积。

多项式卷积使用暴力算法可以做到 \(O(n^22^n)\),可以用 FFT/NTT 优化到 \(O(n\log n2^n)\),但是常数太大,完全没有必要。

  1. void mul(int *F,int *G,int *H)
  2. {
  3. memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
  4. for(int i=0;i<st;i++) f[pct[i]][i]=F[i],g[pct[i]][i]=G[i];
  5. for(int i=0;i<=n;i++) FMT(f[i]),FMT(g[i]);
  6. for(int S=0;S<st;S++)
  7. {
  8. for(int i=n;~i;i--)
  9. {
  10. g[i][S]=1ll*g[i][S]*f[0][S]%Mod;
  11. for(int j=1;j<=i;j++)
  12. add(g[i][S],1ll*f[j][S]*g[i-j][S]%Mod);
  13. }
  14. }
  15. for(int i=0;i<=n;i++) iFMT(g[i]);
  16. for(int i=0;i<st;i++) H[i]=g[pct[i]][i];
  17. }

子集卷积 exp

我们来考虑上面卷积运算的一些组合意义:

如果我们想要查询有 \(f\) 中两个不交集合构成集合对应的方案数,其为 \(\dfrac{F\times F}{2}=\dfrac{F^2}{2}\)

依此类推选择 \(k\) 个构成的不交集合就是 \(\dfrac{F^k}{k!}\)(除 \(k!\) 的原因是选择这些集合的相对顺序是无关的)。

我们考虑选择若干个不交集合(考虑可以不选),有 \(G=x^\varnothing+\sum\limits_{k=1}\dfrac{F^k}{k!}\)

发现 \(\sum\limits_{k=1}\dfrac{F^k}{k!}\),这个东西就是 \(\exp F-1\)。也就是说 \(G=\exp F\)

因此,还是在 \(x\) 维上做或卷积,在 \(y\) 维上我们做多项式 \(exp\),我们就可以通过 \(F\) 来生成 \(G=\exp F\) 了。

\(G=\exp F\),所以 \(G'=(\exp F)'\)

所以 \(G'=\exp F\times F'\Rightarrow G'=G\times F\)

\(\sum\limits_{i}ig_iy^{i-1}=(\sum\limits_{i}g_iy^i)\times (\sum\limits_{i}if_iy^{i-1})\)

所以 \(ng_n=\sum\limits_{i=1}^nf_i\times g_{n-i}\)。这样单次 \(exp\) 可以做到 \(O(n^2)\),所以整体就可以做到 \(O(n^22^n)\)

而子集卷积 exp 的组合意义就是:选择若干个不交集合组合在一起的所有方案

  1. void exp(int *F,int *G)
  2. {
  3. memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
  4. for(int i=1;i<st;i++) f[pct[i]][i]=F[i];
  5. for(int i=0;i<=n;i++) FMT(f[i]);
  6. for(int S=0,tmp;S<st;S++)
  7. {
  8. g[0][S]=1;
  9. for(int i=1;i<=n;i++)
  10. {
  11. for(int j=1;j<=i;j++)
  12. add(g[i][S],1ll*j*f[j][S]%Mod*g[i-j][S]%Mod);
  13. g[i][S]=1ll*g[i][S]*inv[i]%Mod;
  14. }
  15. }
  16. for(int i=0;i<=n;i++) iFMT(g[i]);
  17. for(int i=0;i<st;i++) G[i]=g[pct[i]][i];
  18. }

子集卷积 ln

有 exp 就自然会有 ln。

既然 exp 是组合,那么 ln 就是拆分。

exp 是已知 \(f\) 得到 \(g\),ln 就是通过 \(g\) 得到 \(f\)

由于 \(ng_n=\sum\limits_{i=1}^nf_i\times g_{n-i}\),所以 \(ng_n=nf_ng_0+\sum\limits_{i=1}^{n-1}f_i\times g_{n-i}\)\(g_0=1\))。

\(f_n=g_n-\dfrac{1}{n}\sum\limits_{i=1}^{n-1}f_i\times g_{n-i}\)

这样实现的复杂度也是 \(O(n^22^n)\) 的。

组合意义就是:拆分成若干个不交集合

  1. void ln(int *F,int *G)
  2. {
  3. memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
  4. for(int i=1;i<st;i++) f[pct[i]][i]=F[i];
  5. f[0][0]=1;
  6. for(int i=0;i<=n;i++) FMT(f[i]);
  7. for(int S=0,tmp;S<st;S++)
  8. {
  9. for(int i=1;i<=n;i++)
  10. {
  11. g[i][S]=f[i][S],tmp=0;
  12. for(int j=1;j<i;j++)
  13. add(tmp,1ll*j*g[j][S]%Mod*f[i-j][S]%Mod);
  14. del(g[i][S],1ll*tmp*inv[i]%Mod);
  15. }
  16. }
  17. for(int i=0;i<=n;i++) iFMT(g[i]);
  18. for(int i=0;i<st;i++) G[i]=g[pct[i]][i];
  19. }

原文链接:https://www.cnblogs.com/Xun-Xiaoyao/p/18019800

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号