高维前缀和

参考资料

高维前缀和总结(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)];

注意事项

题目

看一看参考资料。

posted @ 2026-10-08 15:07  lcx_OIer  阅读(6)  评论(0)    收藏  举报