【笔记】高维前缀和

参考博客

以三维前缀和为例,有(f 数组是前缀和)

for (int i=1; i<=N; i++)
	for (int j=1; j<=N; j++)
		for (int k=1; k<=N; k++)
			f[i][j][k] = f[i-1][j][k] + f[i][j-1][k] + f[i][j][k-1]
					- f[i-1][j-1][k] - f[i-1][j][k-1] - f[i][j-1][k-1]
					+ f[i-1][j-1][k-1] + a[i][j][k];

对于 t 维,复杂度是 \(O(2^t N^t)\)
实际上我们可以这么写(a 数组是前缀和)

for (int i=1; i<=N; i++)
	for (int j=1; j<=N; j++)
		for (int k=1; k<=N; k++)
			a[i][j][k] += a[i-1][j][k];
for (int i=1; i<=N; i++)
	for (int j=1; j<=N; j++)
		for (int k=1; k<=N; k++)
			a[i][j][k] += a[i][j-1][k];
for (int i=1; i<=N; i++)
	for (int j=1; j<=N; j++)
		for (int k=1; k<=N; k++)
			a[i][j][k] += a[i][j][k-1];

这样的复杂度是 \(O(t N^t)\)
可能大多数情况下,我们遇到的 N 是 2,也就是一个集合的问题
这时前缀和也就是子集的和了

for (int j=0; j< M; j++)
	for (int o=0; o< (1<<M); o++)
		if (o&(1<<j)) S[o] += S[o^(1<<j)];

当然了,求包含当前集合的集族之和,反过来写就行了

for (int j=0; j< M; j++)
	for (int o=(1<<M)-1; o; o--)
		if ((o&(1<<j))==0) S[o] += S[o|(1<<j)];

例题
HIT2020秋季校赛 C - Quasrain with wrestling match

#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 100005;
const int MAXM = 50004;
int T, N, M, A[MAXN][15], B[MAXN];
int S[MAXM];
int judge(int m)
{
	memset(S, 0, sizeof(S));
	for (int i=1; i<=N; i++) {
		int o = 0;
		for (int j=0; j< M; j++) o += A[i][j]>=m ? (1<<j) : 0;
		//if (o==(1<<M)-1) return 1;
		B[i] = o, S[o]++;
	}
	for (int j=0; j< M; j++)
		for (int o=(1<<M)-1; o; o--) {
			if ((o&(1<<j))==0) S[o] += S[o|(1<<j)];
		}
	for (int i=1; i<=N; i++) {
		int o = ((1<<M)-1)^B[i];
		if (S[o]) return 1;
	}
	return 0;
}
int main()
{
	for (scanf("%d", &T); T; T--) {
		scanf("%d%d", &N, &M);
		for (int i=1; i<=N; i++)
			for (int j=0; j< M; j++) scanf("%d", &A[i][j]);
		int l = 0, r = MAXN; // [l, r)
		while (r-l> 1) {
			int mid = (l + r) >> 1;
			if (judge(mid)) l = mid;
			else r = mid;
		}
		printf("%d\n", l);
	}
}
posted @ 2021-08-02 11:10  zrkc  阅读(152)  评论(0)    收藏  举报