- 高维前缀和 -

(瞎写,类似草稿)

高维前缀和一般解决这样的问题:

对于所有 \(0\le i\le 2^n-1\),求

\[\sum_{j\subset i}a_j \]

其中 \(j\subset i\) 当且仅当 \(j\) 的二进制表示为 \(i\) 的二进制表示的子集 .

枚举子集(暴力)求的复杂度是 \(O(3^n)\) 的,但高维前缀和后就是 \(O(n2^n)\) 的了 .

先放代码:

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

原因:

先从二维前缀和说起 .

一般写的二维前缀和的预处理是这么写的:

for (int i=1;i<=n;i++)
	for (int j=1;j<=m;j++)
		sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];

但其实这种办法并不利于拓展,更好的写法是 逐维前缀和,如下:

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

(这里直接在原数组进行是为了展示一致性)

类似的,高维前缀和均可这么做,例如 \(n\) 维前缀和可以这样:

for (int i1=1;i1<=n1;i1++)
	for (int i2=1;i2<=n2;i2++)
		...
			for (int in=1;i1<=nn;in++)
				a[i1][i2][...][in]+=a[i1+1][i2][...][in];
for (int i1=1;i1<=n1;i1++)
	for (int i2=1;i2<=n2;i2++)
		...
			for (int in=1;i1<=nn;in++)
				a[i1][i2][...][in]+=a[i1][i2+1][...][in];
... ...
for (int i1=1;i1<=n1;i1++)
	for (int i2=1;i2<=n2;i2++)
		...
			for (int in=1;i1<=nn;in++)
				a[i1][i2][...][in]+=a[i1][i2][...][in+1];

求解高维前缀和的核心思想也就是逐维处理,可以类比二维前缀和的求法稍微模拟一下 .

\[f_i\gets f_i+f_{i\oplus(1\mathrm{\, shl\,}j)} \]

的写法也是因为 \(i\oplus(1\mathrm{\, shl\,}j)\)\(i\) 的下一层 .

posted @ 2021-07-08 17:53  Jijidawang  阅读(135)  评论(0编辑  收藏  举报
😅​