SoSdp 用来解决这种问题:
对于非负整数 i,K,定义布尔型二元运算 i⊆K,可以以下四种等价角度理解:
- ibitandK=i。bitand 是按位与的意思。
- 同一个二进制位上,i 的这一位小于等于 K 的这一位。
- 同一个二进制位上,K 这一位为 1,则 i 这一位可以为 1 或 0;K 这一位为 0,则 i 这一位只能为 0。
- i 的所有为 1 的二进制位数集,是 K 的所有为 1 的二进制位数集的子集。
设
f(K)=i⊆K∑ai
a 是一个长度为 2N 的数组,要求对于所有 0≤K<2N,算出 f(K)。上面的 ∑ 可以换成其它合并运算。
方法一
对每个 K,暴力计算 f(K)。对每个 f(K),暴力枚举 i,Θ(1) 判断是否 i⊆K,累计 f(K) 的答案。
时间复杂度 Θ(4N)。
方法二
事实上,有办法只枚举一个数的二进制子集而不枚举其他数,方法比较简单:
初始令 i=K,每次令 i←(i−1)bitandK,直到 i=0 为止。期间访问到的 i 都是 K 的子集。
仍对每个 K 暴力计算 f(K),但对每个 f(K),采用上述方法只枚举其子集。
考虑 K 的二进制子集数量,不难发现是 2popcount(K)。可以证明 K=0∑2N−12popcount(K)=3N。
因此上述算法的时间复杂度是 Θ(3N)。
方法三
是否还可以优化?观察到每个 f(K) 都是由不超过 2N 种 ai 构成,每个 ai 只作为一个小单位累积到 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]
的和:k1≤K1,k2≤K2,k3≤K3,……,kn≤Kn。
不难发现上面这个条件和二进制子集的某个定义是非常类似的:同一个二进制位上,i 的这一位小于等于 K 的这一位。
因此,满足上面这个条件的和,即高维前缀和,与二进制子集的和也是类似的,我们可以类比着做。具体可以看代码。
高维前缀和的复杂度可以做到 Θ(N×2N)。本做法的复杂度也可以。
注意:维数循环在外层,当前状态循环在里层。
例题
ARC100E Or Plus Max
CF449D Jzzhu and Numbers
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】