In mathematics you don't understand things, you just get used to them.

本文差不多算是翻译了一遍 CF blog?id=45223 就是抄了一遍,看不懂可以去原文。

当然我的翻译并不是完全遵从原文的。

Part. 1 Introduction

平时我们怎么求高维前缀和?容斥对吧,复杂度多少?$\mathcal{O}(n^{d}\times2^{d})$($n$ 每维元素个数,默认同阶,$d$ 维度)。

这好吗?这不好。

Part. 2 Ying Wen

For 个 example,二维,容斥这么写对吧?

for(int i=1;i<=n;i++)
{
  for(int j=1;j<=n;j++)  f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]+a[i][j];
}

事实上我们还可以分维来前缀和:

for(int i=1;i<=n;i++)
{
  for(int j=1;j<=n;j++)  f[i][j]=f[i-1][j]+a[i][j];
}
for(int i=1;i<=n;i++)
{
  for(int j=1;j<=n;++j)  f[i][j]=f[i][j-1]+a[i][j];;
}

复杂度多少?$\mathcal{O}(n^{d}\times d)$,厉害吧。

对应到 SOS DP(sum over subsets),我们把每一维整到集合上去来求子集和。

形式化地定义子集和,即给定一个有 $2^{n}$ 个元素的数组 $A$,定义函数:

$$ \text{sub-sum}(mask)=\sum_{i\subseteq mask}A_{i} $$

写成位运算的形式:

$$ \text{sub-sum}(mask)=\sum_{mask\text{ & }i=i}A_{i} $$

学过 FWT 的巨佬可能发现了什么,可是这和我没关系。

看不懂?没关系,我们有严谨的代码定义:

for(int mask = 0;mask < (1<<N); ++mask){
    for(int i = 0;i < (1<<N); ++i){
        if((mask&i) == i){
            F[mask] += A[i];
        }
    }
}

这是什么垃圾复杂度,用高维前缀和可得以下代码:

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)];
  }
}

maths dp bitwise

Journey -「CQOI 2021」
上一篇 «
Solution -「HNOI 2016」最小公倍数(lacks of code)
» 下一篇