Appearance
Advanced Contribution Technique
Quick Revisit
- USE WHEN: sum over all subsets/subarrays/paths — flip to per-element contribution instead
- INVARIANT: Σ f(S) over structures = Σ contribution(e) over elements (linearity of summation)
- TIME: O(n) to O(n log n) depending on structure | SPACE: O(n)
- CLASSIC BUG: double-counting when structures overlap — each element's contribution must be exact
- PRACTICE: 08-ladder-mixed
Instead of computing
Difficulty: [Advanced]CF Rating Range: 1700-2300 Prerequisites: Contribution Technique, basic probability/expectation
Contents
- The Contribution Mindset
- Expected Value via Linearity of Expectation
- Contribution to Tree Paths
- Contribution in Permutations
- Sum Over All Subarrays
- XOR Contribution: Bit Independence
- Common Mistake: Double Counting
- Practice Problems
The Contribution Mindset
The intro file covers the basic reframe: instead of
compute
where
Expected Value via Linearity of Expectation
Linearity of expectation is contribution technique wearing a probability hat.
This holds regardless of dependence between the
The connection. An indicator variable
Example -- Expected number of fixed points in a random permutation:
Let
A random permutation has exactly 1 fixed point on average, regardless of
Example -- Expected number of comparisons in a random quicksort:
Let
Contribution to Tree Paths
Problem: Given a weighted tree on
Brute force:
Contribution reframe: Instead of summing over pairs, ask: "How much does edge
Edge
Total sum of all pairwise distances:
where
cpp
long long total = 0;
int sz[200005];
void dfs(int v, int p, vector<vector<pair<int,int>>>& adj, int n) {
sz[v] = 1;
for (auto [u, w] : adj[v]) {
if (u == p) continue;
dfs(u, v, adj, n);
sz[v] += sz[u];
total += (long long)w * sz[u] * (n - sz[u]);
}
}Contribution in Permutations
Problem: What is the expected number of inversions in a random permutation of
For each pair
The contribution lens: each pair contributes
Sum Over All Subarrays
Problem: Given an array
When = sum
Element
When = min (or max)
For
This gives an
cpp
// Sum of minimums of all subarrays
long long sum_of_mins(vector<int>& a) {
int n = a.size();
vector<int> left(n), right(n);
stack<int> st;
for (int i = 0; i < n; i++) {
while (!st.empty() && a[st.top()] >= a[i]) st.pop();
left[i] = st.empty() ? -1 : st.top();
st.push(i);
}
while (!st.empty()) st.pop();
for (int i = n-1; i >= 0; i--) {
while (!st.empty() && a[st.top()] > a[i]) st.pop();
right[i] = st.empty() ? n : st.top();
st.push(i);
}
long long ans = 0;
for (int i = 0; i < n; i++)
ans += (long long)a[i] * (i - left[i]) * (right[i] - i);
return ans;
}Note the asymmetric comparison (>= left, > right) to handle duplicate values without double-counting.
XOR Contribution: Bit Independence
Problem: Compute
XOR operates independently on each bit. For bit
- Let
= number of elements with bit set. - The pair
contributes to the XOR sum iff exactly one of them has bit set. Count of such pairs: .
cpp
long long xor_pair_sum(vector<int>& a) {
int n = a.size();
long long ans = 0;
for (int b = 0; b < 30; b++) {
long long c = 0;
for (int x : a) if (x >> b & 1) c++;
ans += (1LL << b) * c * (n - c);
}
return ans;
}Avoiding double counting — a checklist
The single biggest failure mode of contribution counting is counting the same outcome twice (or zero times). Before you commit to a formula, walk through these four checks.
- Define the structures you're summing over. Subarrays? Subsets? Pairs
with or all ordered pairs? Tree paths from to with , or all unordered pairs? Get this exact in writing. - Define the unit of contribution. Per element? Per edge? Per bit? Per (element, bit) pair? The unit must partition the contribution of each structure with no overlap.
- Prove each structure's value is fully decomposed. Pick a single structure
and verify that summing the contributions of its units reconstructs exactly. If , you're missing or duplicating something. - Prove each contribution is counted exactly once across structures. For each unit
, count how many structures it appears in and confirm that count matches the multiplier in your formula. This is where ties typically bite.
The tie-breaker pattern. When the contribution depends on
cpp
while (!st.empty() && a[st.top()] >= a[i]) st.pop(); // strict on one side
// ...
while (!st.empty() && a[st.top()] > a[i]) st.pop(); // non-strict on the otherEach subarray now has a unique "winning" index, and no subarray is double-counted.
Practice Problems
| Problem | Contribution Flavor | CF Rating |
|---|---|---|
| CF 1648B - Integral Array | Counting pairs via contribution per value | 1800 |
| CF 817D - Imbalanced Array | Sum of max - min over all subarrays (monotonic stack) | 1900 |
| CF 1208E - Let Them Slide | Per-row contribution with sliding window | 2200 |
| CF 1110E - Magic Stones | Difference array reframe (contribution of each diff) | 1700 |
| CF 1436E - Complicated Computations | MEX contribution per segment | 2400 |
| CSES - Sum of All Subsequences | Each element contributes to | Intro |