The identities that turn binomial sums into closed forms, divisibility facts (Lucas, Kummer, parity), and algorithms for sums of binomial coefficients that have no closed form: prefix sums of a row (single and many offline queries) and sums over a residue class. Binomials mod a prime come from Comb / mint in Modular Arithmetic.
| Identity | Name / use |
|---|---|
| Pascal | |
| symmetry | |
| absorption | |
| trinomial revision (“subset of a subset”) | |
| , | row sums |
| , | weighted row sums |
| hockey stick (column sums) | |
| hockey stick (diagonal) | |
| Vandermonde | |
| Vandermonde with | |
| for ; | alternating partial sums |
| ; | upper negation, negative binomial series |
| shallow diagonals give Fibonacci | |
| multinomial theorem | |
| binomial inversion | |
| , | central binomial size |
| upper bound used in analyses |
| Divisibility | Statement |
|---|---|
| Lucas | over the base- digits: Factorials mod p |
| parity | is odd iff (every bit of is set in ); row has odd entries: Sierpiński’s triangle |
| Kummer | the exponent of in is the number of carries when adding and in base |
| Legendre | ( = digit sum) |
| for | the freshman’s dream |
int kummerCarries(ll n, ll k, ll p) { // v_p(C(n, k)) = carries adding k and n - k in base p
int carries = 0; ll a = k, b = n - k, c = 0;
while (a > 0 || b > 0 || c > 0) { ll d = a % p + b % p + c; c = d >= p; carries += (int)c; a /= p; b /= p; }
return carries;
}
mint binomialPrefix(ll n, ll k) { // sum_{i=0}^{k} C(n, i), n < modulus, O(k)
if (k < 0) return 0;
k = min(k, n); mint res = 1, cur = 1;
for (ll i = 1; i <= k; i++) { cur = cur * mint(n - i + 1) / mint(i); res += cur; }
return res;
}
// Many queries f(n, k) = sum_{i<=k} C(n, i) offline with Mo's algorithm: O((N + q) sqrt N) moves of O(1).
vector<mint> binomialPrefixQueries(const vector<pair<int, int>>& qs, const Comb& cb) { // cb must cover N + 1
int q = sz(qs); vector<mint> res(q); if (!q) return res;
int N = 1; for (auto [n, k] : qs) N = max({N, n + 1, k + 1});
int B = max(1, (int)(N / sqrt((double)q)));
vector<int> order(q); iota(all(order), 0);
sort(all(order), [&](int a, int b) {
int ba = qs[a].first / B, bb = qs[b].first / B;
if (ba != bb) return ba < bb;
return (ba & 1) ? qs[a].second > qs[b].second : qs[a].second < qs[b].second;
});
mint inv2 = mint(1) / mint(2); int n = 0, k = 0; mint f = 1; // f(0, 0) = 1
for (int id : order) {
auto [tn, tk] = qs[id];
while (n < tn) f = mint(2) * f - cb.C(n, k), n++;
while (n > tn) f = (f + cb.C(n - 1, k)) * inv2, n--;
while (k < tk) f += cb.C(n, k + 1), k++;
while (k > tk) f -= cb.C(n, k), k--;
res[id] = f;
}
return res;
}
vector<mint> binomialResidueSums(ll n, int m) { // res[r] = sum_{i = r (mod m)} C(n, i), any n <= 1e18, O(m^2 log n)
auto mul = [&](const vector<mint>& a, const vector<mint>& b) {
vector<mint> c(m, 0);
for (int i = 0; i < m; i++) if (a[i] != mint(0)) for (int j = 0; j < m; j++) c[(i + j) % m] += a[i] * b[j];
return c;
};
vector<mint> res(m, 0), base(m, 0); res[0] = 1; base[0] += 1; base[1 % m] += 1; // 1 + x
for (; n; n >>= 1, base = mul(base, base)) if (n & 1) res = mul(res, base);
return res;
}For with the modulus use Lucas; for large in the residue sums use NTT cyclic convolution instead of the product. Sums of a row prefix have no closed form, which is why the Mo walk (or the residue-class trick) is needed; for a single query with small the loop suffices.
Proof: the identity table
Absorption: choose a committee of with a chair: (committee, then chair) (chair, then the rest). Trinomial revision: choose of and then of those, or the first and then the other from the remaining . Row sums: at ; differentiating once or twice and setting gives the weighted sums. Hockey stick: classify the -subsets of by their largest element : the other elements come from , ways. The diagonal version is the column version after symmetry . Vandermonde: choose people from men and women by the number of men; or compare coefficients in . Alternating partial sums: induction with Pascal, , telescopes. Upper negation: . Fibonacci diagonals: counts tilings of a strip by dominoes and squares, which satisfies the Fibonacci recurrence (last tile a square or a domino). Multinomial theorem: a term picks one from each of the factors. Binomial inversion: in terms of EGFs, , so . Central binomial: is the largest of the terms of row summing to , so it lies between and (Stirling gives the asymptotic). since .
Proof: parity, Kummer, and the prefix-sum moves
Kummer. , and by Legendre , so . Each carry in the base- addition lowers the digit sum by exactly , so this is the number of carries. Parity (): is odd iff adding and in binary has no carry, i.e. and have disjoint bits, i.e. ; the odd entries of row are the submasks of (Lucas gives the same).
Mo moves for . is the definition. By Pascal, , and solving for gives (needs 2 invertible: an odd modulus). Each move is ; Mo’s ordering (blocks of , alternating direction) makes the total number of moves . Residue classes: is the coefficient of in reduced modulo , computed by binary exponentiation with cyclic products. All functions were checked against direct summation, and the table identities numerically for .
Related notes
- Counting Fundamentals
- Factorials mod p (Lucas, Granville)
- Mo’s Algorithm