高维前缀和 (SOSdp)
可以求解关于子集的前缀和,原理本质上是逐位前缀和。
int s[N];
for(int c = 0; c < m; c ++){
for(int state = 0; state < (1 << m); state ++){
if(state >> c & 1){
s[state] += s[state ^ (1<<c)];
}
}
}
(\(m\) 为二进制位数,\(state\) 为二进制状态,\(s[state]\) 需要初始化为集合 \(state\) 本身的函数值,进行上述算法后 \(s[state]\) 表示的是 \(state\) 的所有子集的函数值之和。)
对 \(dp\) 数组的 \(state\) 这一维做前缀和:
\[s[state] = \sum_{state'\subseteq state} dp[state']
\]
可以用 \(O(m * 2^{m})\) 的复杂度获得整个 \(s\) 数组。
辅助理解:可以将 \(n\) 位数字的每一位看作是一个维度,每个维度只有两种取值 \((0 和 1)\),其中 \(s[0] = a[0], s[1] = a[0] + a[1]\),上面的代码就是在做这样的前缀和过程。
反过来,如果要求:
\[s[state] = \sum_{state\subseteq state'} dp[state']
\]
int s[N];
for(int c = 0; c < m; c ++){
for(int state = 0; state < (1 << m); state ++){
if(!(state >> c & 1)){
s[state] += s[state ^ (1<<c)];
}
}
}
只需要将 \(if\) 判断中加上!。
原理见oi-wiki上的“逐维前缀和”部分:oi-wiki

浙公网安备 33010602011771号