[Solution] CSES — Same Sum Subsets
Difference between en4 and en5, changed 13886 character(s)
# Equal-Sum Disjoint Subsets: An FFT-Based Approach↵

## Introduction↵

In this problem, we are asked to find two **disjoint subsets** of an array such that both subsets have the same sum.↵

The key constraints are:↵
- $3 \le n \le 40$↵
- $\sum_{i=1}^{n} x_i \le 2^{n} - 2$↵

After solving it on my own, I looked through several other submissions but couldn't find an approach similar to mine. I found my solution quite elegant, so I decided to share it here.↵

---↵

## Prerequisites↵

- Pigeonhole Principle↵
- Fast Fourier Transform (FFT) / Number Theoretic Transform (NTT)↵
- Meet in the Middle↵

---↵

## Observation 1↵

It is sufficient to find two **distinct** subsets $A$ and $B$ (not necessarily disjoint) satisfying $\sum_{x \in A} x = \sum_{x \in B} x$.↵

**Proof.** Define $A' = A \setminus B$ and $B' = B \setminus A$. Since $A \neq B$, it is impossible that both $A' = \emptyset$ and $B' = \emptyset$, so at least one is nonempty. Furthermore:↵

$$↵
\sum_{x \in A'} x = \sum_{x \in A} x - \sum_{x \in A \cap B} x = \sum_{x \in B} x - \sum_{x \in A \cap B} x = \sum_{x \in B'} x.↵
$$↵

We claim neither $A'$ nor $B'$ can be empty. If $A' = \emptyset$, then the first equality gives $\sum_{x \in A} x = \sum_{x \in A \cap B} x$, and combined with the last equality, $\sum_{x \in B'} x = 0$. Since all elements are positive, $B' = \emptyset$ as well, contradicting $A \neq B$. By symmetry the same holds for $B'$.↵

Therefore $A'$ and $B'$ are two **nonempty disjoint** subsets with equal sum.↵

---↵

## Observation 2 (Existence)↵

A valid solution always exists under the given constraints.↵

**Proof.** The number of nonempty subsets of the array is $2^n - 1$. Each subset has a sum in the range $\left[1,\, \sum_{i=1}^n x_i\right] \subseteq \left[1,\, 2^n - 2\right]$, so there are at most $2^n - 2$ distinct possible sums.↵

Since $2^n - 1 > 2^n - 2$, by the **Pigeonhole Principle** two distinct nonempty subsets must share the same sum. Observation 1 then guarantees the existence of two nonempty disjoint subsets with equal sum. ↵

---↵

## Finding the Collision via Meet in the Middle↵

Existence is guaranteed; we now need to find the two subsets efficiently.↵

We split the array into two halves $L$ and $R$, each of size at most $\lceil n/2 \rceil \le 20$.↵

For a set $S$, define its **subset-sum generating polynomial**:↵

$$↵
P_S(x) = \sum_{T \subseteq S} x^{\,\sum_{a \in T} a}.↵
$$↵

The coefficient of $x^k$ in $P_S(x)$ equals the number of subsets of $S$ whose elements sum to $k$. We compute $P_L(x)$ and $P_R(x)$, then form:↵

$$↵
P(x) = P_L(x) \cdot P_R(x).↵
$$↵

The coefficient of $x^k$ in $P(x)$ counts the number of pairs $(T_L, T_R)$ with $T_L \subseteq L$, $T_R \subseteq R$, and $\sum_{a \in T_L \cup T_R} a = k$; equivalently, it counts subsets of the full array summing to $k$. A coefficient of $2$ or more at any $x^k$ certifies a collision.↵

This multiplication is carried out via **NTT** in $O(M \log M)$ time, where $M$ is the degree of the product polynomial.↵

---↵

## Polynomial Compression↵

The maximum possible subset sum is $\sum x_i \le 2^n - 2 \approx 10^{12}$ for $n = 40$, making the degree of $P(x)$ completely infeasible to handle directly.↵

Instead, we work **modulo $x^k - 1$** for a suitably chosen integer $k$ (equivalently, we track sums modulo $k$). Define the compressed polynomial:↵

$$↵
\tilde{P}_S(x) = P_S(x) \bmod (x^k - 1) = \sum_{r=0}^{k-1} \left( \sum_{\substack{T \subseteq S \\ \sum T \equiv r \pmod{k}}} 1 \right) x^r.↵
$$↵

The coefficient of $x^r$ in $\tilde{P}_L(x) \cdot \tilde{P}_R(x)$ then counts pairs $(T_L, T_R)$ whose combined sum is congruent to $r \pmod k$. All arithmetic is now on polynomials of degree at most $k-1$, and NTT runs in $O(k \log k)$.↵

**Choosing $k$.** We set $k \approx 2 \cdot 10^5$. The total number of subsets is $2^n \ge 2^3 = 8$, and by Observation 2 the number of distinct exact sums is at most $2^n - 2$. So the average number of subsets per residue class modulo $k$ is $2^n / k$. For $n = 40$, this is roughly $2^{40} / (2 \cdot 10^5) \approx 5 \cdot 10^6$, meaning many residue classes will have a very large number of subsets — far more than necessary to guarantee collisions when we enumerate.↵

---↵

## Detecting a Useful Residue Class↵

Let $\mathit{cnt}[r]$ denote the coefficient of $x^r$ in $\tilde{P}(x) = \tilde{P}_L(x) \cdot \tilde{P}_R(x)$, i.e., the number of full-array subsets with sum $\equiv r \pmod{k}$.↵

The total number of subsets (including the empty set) is $2^n$, so:↵

$$↵
\sum_{r=0}^{k-1} \mathit{cnt}[r] = 2^n.↵
$$↵

The number of possible exact sums congruent to $r \pmod k$ is at most $\lfloor (\sum x_i) / k \rfloor + 1 =: h$. Thus, for a collision (two subsets with the *same exact sum*) to be guaranteed among all subsets with sum $\equiv r \pmod k$, we need $\mathit{cnt}[r] > h$ by the Pigeonhole Principle.↵

We scan for any residue $r$ with $\mathit{cnt}[r] > h$ and focus our search there.↵

---↵

## Recovering the Answer↵

We now have a target residue $r^*$ such that more subsets map to $r^*$ than there are distinct exact sums congruent to $r^* \pmod k$.↵

**Step 1.** Enumerate all subsets $T_L \subseteq L$ with $\sum T_L \equiv r^* \pmod k$. For each, record its **exact** sum and bitmask.↵

**Step 2.** Enumerate all subsets $T_R \subseteq R$. For each mask $T_R$ with $\sum T_R \equiv s \pmod k$, look up the bucket for residue $(r^* - s) \bmod k$ among the left-half subsets. For each pair $(T_L, T_R)$ in that bucket, their combined exact sum is $\sum T_L + \sum T_R$. Group these pairs by their exact combined sum (indexed by $\lfloor (\sum T_L + \sum T_R) / k \rfloor$, which fits in a flat array of size $h$).↵

**Step 3.** The first time a bucket in Step 2 is hit for the second time, we have found two distinct subsets $A = T_L^{(1)} \cup T_R^{(1)}$ and $B = T_L^{(2)} \cup T_R^{(2)}$ with the same exact sum. Apply Observation 1
> **Intuition.** If $A$ and $B$ share elements, those elements contribute equally to both sums and can simply be cancelled out. What remains on each side is a pair of disjoint subsets with equal sum.↵

**Proof.** Define $A' = A \setminus B$ and $B' = B \setminus A$. By construction $A' \cap B' = \emptyset$. Since $A \neq B$, it is impossible that both $A' = \emptyset$ and $B' = \emptyset$, so at least one is nonempty. Furthermore, cancelling the shared part $A \cap B$:↵

$$↵
\sum_{x \in A'} x = \sum_{x \in A} x - \sum_{x \in A \cap B} x = \sum_{x \in B} x - \sum_{x \in A \cap B} x = \sum_{x \in B'} x.↵
$$↵

We claim neither $A'$ nor $B'$ can be empty. If $A' = \emptyset$, then $\sum_{x \in A} x = \sum_{x \in A \cap B} x$, so $\sum_{x \in B'} x = 0$. Since all elements are positive, $B' = \emptyset$ as well — contradicting $A \neq B$. By symmetry the same holds for $B'$.↵

Therefore $A'$ and $B'$ are two **nonempty disjoint** subsets with equal sum. $\blacksquare$↵

---↵

## Observation 2 (Existence)↵

A valid solution always exists under the given constraints.↵

**Proof.** The number of nonempty subsets of the array is $2^n - 1$. Each subset has a sum in the range $\left[1,\, \sum_{i=1}^n x_i\right] \subseteq \left[1,\, 2^n - 2\right]$, so there are at most $2^n - 2$ distinct possible sums.↵

Since $2^n - 1 > 2^n - 2$, by the **Pigeonhole Principle** two distinct nonempty subsets must share the same sum. Observation 1 then guarantees the existence of two nonempty disjoint subsets with equal sum. $\blacksquare$↵

---↵

## Finding the Collision via Meet in the Middle↵

Existence is guaranteed; we now need to find the two subsets efficiently.↵

**The naive approach** would be to enumerate all $2^n$ subsets, record their sums, and look for a duplicate. For $n = 40$ this is $2^{40} \approx 10^{12}$ subsets — far too slow.↵

**Meet in the middle** cuts this in half. Split the array into two halves $L$ and $R$, each of size at most $\lceil n/2 \rceil \le 20$. Now we only enumerate $2^{20} \approx 10^6$ subsets per half. The idea is to express any subset of the full array as a union $T_L \cup T_R$ with $T_L \subseteq L$ and $T_R \subseteq R$, so its sum is $\sum T_L + \sum T_R$.↵

A natural way to count subsets by sum is via **generating polynomials**. For a set $S$, define:↵

$$↵
P_S(x) = \sum_{T \subseteq S} x^{\,\sum_{a \in T} a}.↵
$$↵

The coefficient of $x^k$ in $P_S(x)$ equals the number of subsets of $S$ whose elements sum to $k$. For example, if $S = \{2, 3\}$, then $P_S(x) = 1 + x^2 + x^3 + x^5$ (corresponding to subsets $\emptyset, \{2\}, \{3\}, \{2,3\}$).↵

Now observe that since every full-array subset splits as $T_L \cup T_R$:↵

$$↵
P(x) = P_L(x) \cdot P_R(x),↵
$$↵

because when we multiply $P_L$ and $P_R$, the coefficient of $x^k$ in the product counts exactly the pairs $(T_L, T_R)$ with $\sum T_L + \sum T_R = k$ — i.e., all subsets of the full array summing to $k$. A coefficient of $2$ or more at any $x^k$ certifies that two distinct such subsets exist, giving us our collision.↵

This multiplication is carried out via **NTT** in $O(M \log M)$ time, where $M$ is the degree of the product polynomial.↵

---↵

## Polynomial Compression↵

The maximum possible subset sum is $\sum x_i \le 2^n - 2 \approx 10^{12}$ for $n = 40$, making the degree of $P(x)$ completely infeasible to handle directly.↵

**The key idea:** we don't need to track exact sums at all — we only need to find a residue class that is overcrowded, and then search within it for a collision. So instead of computing $P(x)$ exactly, we truncate it: we compute $P(x) \bmod x^k$ for a suitably chosen integer $k$.↵

Concretely, define the **compressed polynomial** of a set $S$ as:↵

$$↵
\tilde{P}_S(x) = P_S(x) \bmod x^k = \sum_{r=0}^{k-1} \left( \sum_{\substack{T \subseteq S \\ \sum T \equiv r \pmod{k}}} 1 \right) x^r.↵
$$↵

In other words, we simply ignore all terms of degree $\ge k$, which is equivalent to only recording each subset sum modulo $k$. The coefficient of $x^r$ tells us how many subsets of $S$ have sum with remainder $r$ when divided by $k$.↵

> **Why does truncation work here, and not cyclic reduction?**↵
> If we reduced modulo $x^k - 1$ (cyclic), the coefficient of $x^r$ in the product would count pairs where $(\sum T_L + \sum T_R) \equiv r \pmod k$, which is the same information. The difference is purely implementation: truncating at $x^k$ is equivalent to zeroing out high-degree terms after the ordinary (acyclic) convolution, while cyclic reduction would require a negacyclic NTT. Since we perform an ordinary NTT and then fold the result (see the "Detecting a Useful Residue Class" section), truncation is the natural framing.↵

Now, $\tilde{P}_L(x) \cdot \tilde{P}_R(x) \bmod x^k$ has degree at most $k - 1$, so we can compute it with an NTT on arrays of size $O(k)$ in $O(k \log k)$ time. The coefficient of $x^r$ in this product counts pairs $(T_L, T_R)$ with $T_L \subseteq L$, $T_R \subseteq R$, and $(\sum T_L + \sum T_R) \bmod k = r$.↵

**Choosing $k$.** We set $k \approx 2 \cdot 10^5$. The number of subsets of the full array is $2^n$, so the average count per residue class is $2^n / k$. For $n = 40$, this is roughly $2^{40} / (2 \cdot 10^5) \approx 5 \cdot 10^6$. Meanwhile the number of distinct exact sums in any residue class is at most $\lfloor (\sum x_i) / k \rfloor + 1 \approx (2^{40}) / (2 \cdot 10^5) \approx 5 \cdot 10^6$ as well — so at worst these quantities are comparable. For smaller $n$ the ratio is much more favorable. The point is that by the Pigeonhole Principle (formalized below), some residue class must be overcrowded enough to guarantee a collision.↵

---↵

## Detecting a Useful Residue Class↵

After the NTT we have, for each $r \in [0, k)$, the value $\mathit{cnt}[r]$: the number of full-array subsets (including $\emptyset$) with sum congruent to $r \pmod k$.↵

$$↵
\sum_{r=0}^{k-1} \mathit{cnt}[r] = 2^n.↵
$$↵

> **Important subtlety.** The NTT computes an ordinary (acyclic) convolution, so `box[i]` for $i \ge k$ is nonzero and represents pairs whose sum lands in $[k, 2k)$ but has remainder $i - k$ mod $k$. To get the true $\mathit{cnt}[r]$ we must **fold**: $\mathit{cnt}[r] = \texttt{box}[r] + \texttt{box}[r + k]$ (only one wrap-around term is possible since $\max(\sum T_L) + \max(\sum T_R) < 2k$ for our choice of $k$... actually for large $n$ this isn't true in general, but in the code the convolution output naturally has limited wrap-around that the fold handles correctly).↵

Now, the number of **distinct exact sums** in residue class $r$ is at most:↵

$$↵
h = \left\lfloor \frac{\sum x_i}{k} \right\rfloor + 1,↵
$$↵

since the exact sums congruent to $r \pmod k$ form an arithmetic progression $\{r,\, r+k,\, r+2k,\, \ldots\}$ capped at $\sum x_i$.↵

**The key Pigeonhole argument:** if $\mathit{cnt}[r] > h$, then by the Pigeonhole Principle, at least two of the $\mathit{cnt}[r]$ subsets in class $r$ must have the **same exact sum** — giving us our desired collision. We scan for any such $r^*$ and focus the search there.↵

Such an $r^*$ must exist: if every class had $\mathit{cnt}[r] \le h$, then $\sum_r \mathit{cnt}[r] \le k \cdot h \approx \sum x_i + k$. For large $n$ this is much less than $2^n$, so the inequality is violated and a crowded class must exist.↵

---↵

## Recovering the Answer↵

We have a target residue $r^*$ with $\mathit{cnt}[r^*] > h$, guaranteeing a collision in exact sums somewhere in that class. Now we need to actually *find* the two colliding subsets.↵

The approach is again meet in the middle, but now restricted to residue class $r^*$.↵

**Step 1.** Enumerate all subsets $T_L \subseteq L$ with $\sum T_L \bmod k = r_L$ for each possible $r_L$. Store them in buckets indexed by $r_L$: each entry records the exact sum and the bitmask.↵

**Step 2.** Enumerate all subsets $T_R \subseteq R$. For a given $T_R$ with $\sum T_R \bmod k = r_R$, the combined sum $\sum T_L + \sum T_R$ has remainder $r^*$ iff $r_L = (r^* - r_R) \bmod k$. So we look up the bucket for residue $(r^* - r_R) \bmod k$ among the left-half subsets.↵

**Step 3.** For each matching pair $(T_L, T_R)$, compute the exact combined sum $s = \sum T_L + \sum T_R$. This exact sum lies in $\{r^*,\, r^* + k,\, r^* + 2k,\, \ldots\}$, so we can index it by $q = \lfloor s / k \rfloor$, which ranges over at most $h$ values. We maintain a table `mp[q]` that stores the first pair seen with that index. The moment we encounter a $q$ already in the table, we have our collision: two distinct subsets with the same exact sum.↵

**Step 4.** Apply Observation 1 to the two colliding subsets
 to obtain the final disjoint pair.↵

This enumeration touches at most $O(2^{n/2})$ subsets per half, consistent with the meet-in-the-middle paradigm.↵

---↵

## Correctness Summary↵

1. **Existence** is guaranteed by Observation 2.↵
2. **Compression** reduces polynomial degree to $k$ without losing the ability to detect collisions, because any residue $r^*$ with $\mathit{cnt}[r^*] > h$ must contain two subsets with the same exact sum (Pigeonhole).↵
3. **Recovery** finds the collision in $O(2^{n/2})$ time after the NTT.↵
4. **Output** transforms the equal-sum pair into a disjoint pair via Observation 1.↵

---↵

## Complexity↵

| Phase | Time |↵
|---|---|↵
| Subset enumeration (each half) | $O(2^{n/2})$ |↵
| NTT convolution | $O(k \log k)$ |↵
| Collision recovery | $O(2^{n/2})$ |↵
| **Total** | $O(2^{n/2} + k \log k)$ |↵

With $n = 40$ and $k = 2 \cdot 10^5$, both terms are comfortably within time limits.↵

---↵

## Implementation↵

<spoiler summary="Implementation">↵

```cpp↵
#pragma GCC optimize("O3")↵
#pragma GCC optimize("unroll-loops")↵
#pragma GCC target("avx2,bmi,bmi2,lzcnt,popcnt")↵
#include <bits/stdc++.h>↵
using namespace std;↵
#define endl '\n'↵
using ull = uint64_t;↵

ull bin_pow(ull a, ull b, ull m) {↵
    a %= m;↵
    ull res = 1;↵
    while (b > 0) {↵
        if (b & 1)↵
            res = (__uint128_t)res * a % m;↵
        a = (__uint128_t)a * a % m;↵
        b >>= 1;↵
    }↵
    return res;↵
}↵

struct montgomery {↵
    ull n, nr;↵
    constexpr montgomery(ull n) : n(n), nr(1) {↵
        for (int i = 0; i < 6; i++)↵
            nr *= 2 - n * nr;↵
    }↵
    ull reduce(__uint128_t x) const {↵
        ull q = __uint128_t(x) * nr;↵
        ull m = ((__uint128_t)q * n) >> 64;↵
        ull res = (x >> 64) + n - m;↵
        if (res >= n) res -= n;↵
        return res;↵
    }↵
    ull multiply(ull x, ull y) const { return reduce((__uint128_t)x * y); }↵
    ull transform(ull x) const { return (__uint128_t(x) << 64) % n; }↵
};↵

template<ull mod, ull gen>↵
struct fast_ntt {↵
    fast_ntt() {}↵
    vector<int> bit_sort(int n) {↵
        int h = -1;↵
        vector<int> rev(n, 0);↵
        int skip = __lg(n) - 1;↵
        for (int i = 1; i < n; ++i) {↵
            if (!(i & (i - 1))) ++h;↵
            rev[i] = rev[i ^ (1 << h)] | (1 << (skip - h));↵
        }↵
        return rev;↵
    }↵
    void ntt(vector<ull>& a, vector<int>& rev, montgomery& red,↵
             ull inv_n, ull root, ull inv_root, bool invert) {↵
        int n = (int)a.size();↵
        for (int i = 0; i < n; ++i)↵
            if (i < rev[i]) swap(a[i], a[rev[i]]);↵
        ull w = invert ? inv_root : root;↵
        vector<ull> W(n >> 1, red.transform(1));↵
        for (int i = 1; i < (n >> 1); ++i)↵
            W[i] = red.multiply(W[i - 1], w);↵
        int lim = __lg(n);↵
        for (int i = 0; i < lim; ++i)↵
            for (int j = 0; j < n; ++j)↵
                if (!(j & (1 << i))) {↵
                    ull t = red.multiply(a[j ^ (1 << i)],↵
                                        W[(j & ((1 << i) - 1)) * (n >> (i + 1))]);↵
                    a[j ^ (1 << i)] = a[j] >= t ? a[j] - t : a[j] + mod - t;↵
                    a[j] = a[j] + t < mod ? a[j] + t : a[j] + t - mod;↵
                }↵
        if (invert)↵
            for (int i = 0; i < n; i++)↵
                a[i] = red.multiply(a[i], inv_n);↵
    }↵
    vector<ull> conv(vector<ull> a, vector<ull> b) {↵
        montgomery red(mod);↵
        for (auto& x : a) x = red.transform(x);↵
        for (auto& x : b) x = red.transform(x);↵
        int n = 1;↵
        while (n < (int)a.size() || n < (int)b.size()) n <<= 1;↵
        n <<= 1;↵
        a.resize(n); b.resize(n);↵
        ull inv_n    = red.transform(bin_pow(n, mod - 2, mod));↵
        ull root     = red.transform(bin_pow(gen, (mod - 1) / n, mod));↵
        ull inv_root = red.transform(bin_pow(red.reduce(root), mod - 2, mod));↵
        auto rev = bit_sort(n);↵
        ntt(a, rev, red, inv_n, root, inv_root, false);↵
        ntt(b, rev, red, inv_n, root, inv_root, false);↵
        for (int i = 0; i < n; i++) a[i] = red.multiply(a[i], b[i]);↵
        ntt(a, rev, red, inv_n, root, inv_root, true);↵
        for (auto& x : a) x = red.reduce(x);↵
        return a;↵
    }↵
};↵

// Returns, for each bitmask, the sum of the corresponding subset.↵
vector<ull> get_subset(const vector<ull>& arr) {↵
    int n = arr.size();↵
    ull total = 1 << n;↵
    vector<ull> res(total);↵
    for (int mask = 0; mask < (int)total; ++mask) {↵
        ull sum = 0;↵
        for (int j = 0, m = mask; m; ++j, m >>= 1)↵
            if (m & 1) sum += arr[j];↵
        res[mask] = sum;↵
    }↵
    return res;↵
}↵

// Prints two disjoint subsets encoded as bitmasks over p1 (left half) and p2 (right half).↵
// (m11, m21) are the left/right bitmasks for subset A; (m12, m22) for subset B.↵
// The two bitmasks for each subset must be disjoint.↵
void print(int m11, int m12, int m21, int m22,↵
           vector<ull>& v1, vector<ull>& v2) {↵
    int n1 = v1.size(), n2 = v2.size();↵
    vector<ull> a1, a2;↵
    assert((m11 & m12) == 0);↵
    assert((m21 & m22) == 0);↵
    for (int i = 0; i < n1; i++) {↵
        if (m11 & (1 << i)) a1.push_back(v1[i]);↵
        if (m12 & (1 << i)) a2.push_back(v1[i]);↵
    }↵
    for (int i = 0; i < n2; i++) {↵
        if (m21 & (1 << i)) a1.push_back(v2[i]);↵
        if (m22 & (1 << i)) a2.push_back(v2[i]);↵
    }↵
    cout << a1.size() << endl;↵
    for (auto c : a1) cout << c << " ";↵
    cout << endl;↵
    cout << a2.size() << endl;↵
    for (auto c : a2) cout << c << " ";↵
    cout << endl;↵
}↵

const ull mod = 9223372036737335297, pr = 3;↵
const ull mxn = 2e5 + 100;  // modulus k for sum compression↵

signed main() {↵
    ios_base::sync_with_stdio(0);↵
    cin.tie(0); cout.tie(0);↵

    int n;↵
    cin >> n;↵
    vector<ull> arr(n);↵
    ull sum = 0;↵
    for (int i = 0; i < n; i++) cin >> arr[i], sum += arr[i];↵

    // Split into two halves.↵
    int n1 = n / 2;↵
    vector<ull> p1(arr.begin(), arr.begin() + n1),↵
                p2(arr.begin() + n1, arr.end());↵

    // Enumerate all subset sums for each half.↵
    vector<ull> v1 = get_subset(p1), v2 = get_subset(p2);↵
    int sz1 = v1.size(), sz2 = v2.size();↵

    // Build compressed polynomials (coefficients indexed by sum mod mxn).↵
    fast_ntt<mod, pr> st;↵
    vector<ull> q1(mxn, 0), q2(mxn, 0);↵
    for (int i = 0; i < sz1; i++) q1[v1[i] % mxn]++;↵
    for (int i = 0; i < sz2; i++) q2[v2[i] % mxn]++;↵

    // Convolve; coefficient at index r gives cnt[r mod mxn].↵
    vector<ull> box = st.conv(q1, q2);↵
    int gz = box.size();↵

    // Find a residue r* such that cnt[r*] exceeds the number of distinct↵
    // exact sums that are congruent to r* mod mxn (i.e., > floor(sum/mxn)+1).↵
    // Any such r* is guaranteed to contain a collision in exact sums.↵
    ull obj = 0;↵
    for (ull i = 0; i < mxn; i++) {↵
        // Fold the convolution: add the wrap-around term (index i + mxn).↵
        ull s1 = (i + mxn < (ull)gz) ? box[i] + box[i + mxn] : box[i];↵
        // Number of integers in [0, sum] that are congruent to i mod mxn.↵
        ull umbral = (sum + 1) / mxn;↵
        if (i <= sum % mxn) umbral++;↵
        if (s1 > umbral) {↵
            obj = i;↵
            break;↵
        }↵
    }↵

    // Group left-half subsets by their sum mod mxn.↵
    vector<vector<pair<ull, int>>> bucket(mxn);↵
    for (int i = 0; i < sz1; i++)↵
        bucket[v1[i] % mxn].push_back({v1[i], i});↵

    // h = number of distinct exact sums congruent to any residue mod mxn;↵
    // used to size the flat collision table indexed by floor(exact_sum / mxn).↵
    int ht = (int)(sum / mxn) + 1;↵
    // mp[q] stores the (left mask, right mask) of the first subset seen↵
    // with combined exact sum q * mxn + obj.  A second hit is our collision.↵
    vector<pair<int, int>> mp(ht, {-1, -1});↵

    for (int i = 0; i < sz2; i++) {↵
        ull k2 = v2[i] % mxn;↵
        // Required left residue so that combined residue equals obj.↵
        ull o1 = (k2 <= obj) ? obj - k2 : obj + mxn - k2;↵
        for (auto [s1, m1] : bucket[o1]) {↵
            int h1 = (int)((s1 + v2[i]) / mxn);↵
            if (mp[h1] != make_pair(-1, -1)) {↵
                // Collision found: two subsets A and B with the same exact sum.↵
                // Extract disjoint parts via Observation 1.↵
                auto [m21, m22] = mp[h1];↵
                int i1 = m21 & m1, i2 = i & m22;↵
                print(m1 ^ i1, m21 ^ i1, i ^ i2, m22 ^ i2, p1, p2);↵
                return 0;↵
            } else {↵
                mp[h1] = {m1, i};↵
            }↵
        }↵
    }↵
    return 0;↵
}↵
```↵

</spoiler>↵

History

 
 
 
 
Revisions
 
 
  Rev. Lang. By When Δ Comment
en8 English abner_vidal 2026-04-07 03:38:33 2268
en7 English abner_vidal 2026-04-07 03:23:25 29
en6 English abner_vidal 2026-04-07 03:18:19 57 Tiny change: '# Equal-Sum Disjoint Subsets: An FFT-Based Approach\n\n## Introdu' -> '## Introdu'
en5 English abner_vidal 2026-04-07 03:17:57 13886 (published)
en4 English abner_vidal 2026-04-07 03:10:17 10
en3 English abner_vidal 2026-04-07 03:06:17 30
en2 English abner_vidal 2026-04-07 03:03:58 18243
en1 English abner_vidal 2026-04-07 02:55:01 4285 Initial revision (saved to drafts)