【笔记】高维前缀和
以三维前缀和为例,有(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);
}
}

浙公网安备 33010602011771号