Compute, for every mask, the sum over all of its submasks:
in instead of the naive .
The algorithm
// subset sum: F[S] = sum of f[T] over T subset of S
vector<long long> F(f);
for (int i = 0; i < n; i++)
for (int mask = 0; mask < (1 << n); mask++)
if (mask >> i & 1)
F[mask] += F[mask ^ (1 << i)];Three lines. The outer loop over bit positions is essential — swapping the loops gives a different (wrong) transform.
Why it works
Process bits one at a time. After stage , holds the sum over all that agree with mask on bits and are arbitrary submasks on bits . Stage merges the two halves. After stages, is the full subset sum.
Formally, this is the zeta transform of the subset lattice, computed by a dimension-by-dimension sweep — exactly the same structure as a multidimensional prefix sum, with each dimension having size 2.
The four directions
// subset sum (zeta)
for (int i = 0; i < n; i++) for (int m = 0; m < (1<<n); m++)
if (m >> i & 1) F[m] += F[m ^ (1<<i)];
// subset sum INVERSE (Möbius)
for (int i = 0; i < n; i++) for (int m = 0; m < (1<<n); m++)
if (m >> i & 1) F[m] -= F[m ^ (1<<i)];
// superset sum
for (int i = 0; i < n; i++) for (int m = 0; m < (1<<n); m++)
if (!(m >> i & 1)) F[m] += F[m | (1<<i)];
// superset sum inverse
for (int i = 0; i < n; i++) for (int m = 0; m < (1<<n); m++)
if (!(m >> i & 1)) F[m] -= F[m | (1<<i)];OR and AND convolution
- Zeta-transform and (subset sums).
- Multiply pointwise.
- Möbius-transform the result.
. Replace subset with superset to get AND-convolution. For XOR-convolution use FWHT — same shape, different transform.
| Convolution | Transform |
|---|---|
| OR () | subset-sum zeta / Möbius |
| AND () | superset-sum zeta / Möbius |
| XOR () | Walsh-Hadamard |
| Disjoint union (, ) | subset convolution — see below |
Subset convolution —
OR-convolution over-counts because it allows overlaps. The fix is to rank by popcount: define if , else 0. Then
using OR-convolution, and . The popcount rank makes disjointness automatic, because only when they are disjoint.
— practical to , and the standard tool for “partition a set into two parts” counting problems.
Applications
| Problem | Method |
|---|---|
| For each mask, sum over its submasks | direct SOS |
| For each mask, count array elements that are submasks of it | SOS over a frequency array |
| Maximum with | SOS storing the best two values |
| Count pairs with | superset SOS on the complement |
| Count pairs with full | subset SOS |
| Number of ways to cover a set with disjoint pieces | subset convolution |
| Steiner tree merge step | → subset convolution |
| Inclusion-exclusion over constraints | Möbius transform |
The “maximum over submasks” variant
Store the best two values with distinct masks so you can combine them without overlap:
// dp[mask] = best value among submasks of mask
for (int i = 0; i < n; i++)
for (int m = 0; m < (1<<n); m++)
if (m >> i & 1) dp[m] = max(dp[m], dp[m ^ (1<<i)]);Then “maximum with ” is .
Memory
values. For and long long that is 8 MB — fine. For it is 134 MB — use int or process in halves.
See also: Enumerating Submasks · FWHT · Bitmask DP