SoSdp 学习笔记

SoSdp 用来解决这种问题:

对于非负整数 \(i\)\(K\),定义布尔型二元运算 \(i\subseteq K\),可以以下四种等价角度理解:

  • \(i \operatorname{bitand} K = i\)\(\operatorname{bitand}\) 是按位与的意思。
  • 同一个二进制位上,\(i\) 的这一位小于等于 \(K\) 的这一位。
  • 同一个二进制位上,\(K\) 这一位为 \(1\),则 \(i\) 这一位可以为 \(1\)\(0\)\(K\) 这一位为 \(0\),则 \(i\) 这一位只能为 \(0\)
  • \(i\) 的所有为 \(1\) 的二进制位数集,是 \(K\) 的所有为 \(1\) 的二进制位数集的子集。

\[f(K) = \sum_{i \subseteq K} a_i \]

\(a\) 是一个长度为 \(2^N\) 的数组,要求对于所有 \(0 \le K < 2^N\),算出 \(f(K)\)。上面的 \(\sum\) 可以换成其它合并运算。

方法一

对每个 \(K\),暴力计算 \(f(K)\)。对每个 \(f(K)\),暴力枚举 \(i\)\(\Theta(1)\) 判断是否 \(i \subseteq K\),累计 \(f(K)\) 的答案。

时间复杂度 \(\Theta(4^N)\)

方法二

事实上,有办法只枚举一个数的二进制子集而不枚举其他数,方法比较简单:

初始令 \(i = K\),每次令 \(i \gets (i - 1) \operatorname{bitand} K\),直到 \(i = 0\) 为止。期间访问到的 \(i\) 都是 \(K\) 的子集。

仍对每个 \(K\) 暴力计算 \(f(K)\),但对每个 \(f(K)\),采用上述方法只枚举其子集。

考虑 \(K\) 的二进制子集数量,不难发现是 \(2^{\mathrm{popcount}(K)}\)。可以证明 \(\sum \limits_{K = 0}^{2^N - 1} 2^{\operatorname{popcount}(K)} = 3^N\)

因此上述算法的时间复杂度是 \(\Theta(3^N)\)

方法三

是否还可以优化?观察到每个 \(f(K)\) 都是由不超过 \(2^N\)\(a_i\) 构成,每个 \(a_i\) 只作为一个小单位累积到 \(f(K)\) 还是过于暴力了。

考虑一个边长为 \(2\)\(N\) 维超立方体,即一个 \(N\) 维数组 a[2][2][2]...[2][2]

然后把一个数 \(K\) 写成二进制形式,然后填入上面的下标,比如 \(K = (110)_2\)\(N = 4\),就可以记作 a[0][1][1][0]

考虑前缀和的定义:a 的前缀和数组 sum 中,有sum[K1][K2][K3]..[Kn] 等于所有满足以下条件的 a[k1][k2][k3]...[kn] 的和:\(k_1 \le K_1\)\(k_2 \le K_2\)\(k_3 \le K_3\),……,\(k_n \le K_n\)

不难发现上面这个条件和二进制子集的某个定义是非常类似的:同一个二进制位上,\(i\) 的这一位小于等于 \(K\) 的这一位。

因此,满足上面这个条件的和,即高维前缀和,与二进制子集的和也是类似的,我们可以类比着做。具体可以看代码。

高维前缀和的复杂度可以做到 \(\Theta(N \times 2^N)\)。本做法的复杂度也可以。

注意:维数循环在外层,当前状态循环在里层。

例题

ARC100E Or Plus Max

CF449D Jzzhu and Numbers

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值