简单子集运算

参考


对于集合 (U = {1,2,cdots,n}), 有下标系统 (A = {Xmid Xsubseteq U}),和定义在 (A) 上的函数:(f:A o mathcal R)(mathcal R) 大概是某个很牛逼的代数系统,有交换律什么的,应该直接默认是实数域就行了)

本文讨论对于这个函数 (f) 的变换。

用状压模拟 (f) 的下标系统:

n = read();
for (int I = 0; I < (1 << n); ++ i) f[i] = read();


子集和

[hat f(I) = sum_{Tsubseteq I} f(T) ]

求这个 (hat f)

从另一个角度看待 (f) 的状压表示, 二进制的每一位看成多维数组的一个维度的某个数,那么这个子集和就是个高维前缀和。

插入

高维前缀和的算法:

对于一个高维点 ((x_1,x_2,cdots,x_n)), 其高维前缀和是对 ((y_1le x_1,y_2le x_2,cdots,y_nle x_n)) 这些点值的求和。

将这些点值按照最后一维分类,将整个 n 维超立方体分成了 (|{kmid kle x_n}|) 个 n - 1 维的超立方体, 看上去就十分显然了。

代码

for (int i = 0; i < n; ++ i)
  for (int s = 0; s < (1 << n); ++ s)
    	if ((s >> i) & 1) f[s] += f[s ^ (1 << i)];

子集和的逆变换

就是给你 (hat f), 求 (f)

有了前面的铺垫,这个东西就很简单了,就是对每一维从大到小扫过去差分,不过在这里一个维只有两个点值所以看起来就很简洁。

代码

for (int i = 0; i < n; ++ i)
  for (int s = 0; s < (1 << n); ++ i)
    if ((s >> i) & 1) f[s] -= f[s ^ (1 << i)];

超集和

[f(I)zeta = sum_{Isubseteq T} f(T) ]

给 f 求 fζ。

就是把 1 看成 0, 0 看成 1 了而已, 在一般的有限高维下标系统里, 可以直接看成把偏序关系反过来做子集和。

代码

for (int i = 0; i < n; ++ i)
  for (int s = 0; s < (1 << n); ++ s)
    	if (! ((s >> i) & 1)) f[s] += f[s ^ (1 << i)];

超集和逆变换

不多说了,代码

for (int i = 0; i < n; ++ i)
  for (int s = 0; s < (1 << n); ++ s)
    	if (! ((s >> i) & 1)) f[s] -= f[s ^ (1 << i)];

补集和

这个东西是啥啊,原文没说我不知道。

原文地址:https://www.cnblogs.com/tztqwq/p/14527528.html