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

  1. Zeta-transform and (subset sums).
  2. Multiply pointwise.
  3. Möbius-transform the result.

. Replace subset with superset to get AND-convolution. For XOR-convolution use FWHT — same shape, different transform.

ConvolutionTransform
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

ProblemMethod
For each mask, sum over its submasksdirect SOS
For each mask, count array elements that are submasks of itSOS over a frequency array
Maximum with SOS storing the best two values
Count pairs with superset SOS on the complement
Count pairs with fullsubset SOS
Number of ways to cover a set with disjoint piecessubset convolution
Steiner tree merge step → subset convolution
Inclusion-exclusion over constraintsMö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