高维前缀和
参考资料
高维前缀和总结(sosdp)
高维前缀和/SOS dp
SOSDP(子集DP)、高维前缀和、FMT(快速莫比乌斯变换)、FWT(快速沃尔什变换)学习笔记
简介
一般用于高效解决求子集和/超集和等问题。
引入
\(\forall i,0\le i<2^n\),求解 \(\sum_{j\in i} a_j\)。
解法
朴素方法就是对于每个长度为 \(n\) 的 \(i\) 枚举子集,分析一下复杂度,如果当前位是 1,那么子集当前位有 1,0 两种选法;如果当前位是 0,那么只有一种,所以总共有 \(3\) 种选法,根据乘法原理,复杂度就是 \(O(3^n)\)。
事实上存在一种更优的方法,可以做到 \(O(n\times 2^n)\)。
我们先从普通的二维前缀和考虑,我们除了容斥写法,还有一种写法:
for(int i=1;i<=n;++i)
S[i]=S[i-1]+A[i];
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
S[i][j]=S[i][j-1]+A[i][j];
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
S[i][j]=S[i-1][j]+S[i][j];
我们就相当于对于每个维扫一遍,复杂度应该是 \(O(n\times d^n)\),\(n\) 是维度,\(d\) 是每一维的限制。
那回到原问题,我们可以将每个数视作一个 \(n\) 维数组,只不过由于每一维只有 0/1,我们用类似的方法即可做到 \(O(n2^n)\):
for(int i=0;i<n;++i)//枚举当前处理哪一维度
for(int j=0;j<(1<<n);++i)
if((1<<i)&j) S[j]+=(S[j^(1<<i)]);//如果该维度为1,统计上该维度为0的前缀和
应用
求子集合
就是前面讲的。
求超集和
把 0 看成 1,1 看成 0 即可。
for(int j = 0; j < n; j++)
for(int i = 0; i < 1 << n; i++)
if(!(i >> j & 1)) f[i] += f[i ^ (1 << j)];
注意事项
题目
看一看参考资料。

浙公网安备 33010602011771号