← Retour à l'épreuve

India INAIO 2025 · Correction IOAI Training

Theory Problems

Cette page donne toute la méthode. Lisez-la une fois que vous avez cherché.

Métrique
answer key (numeric + justifications)
Évaluée sur
written answers
Notre score
all answers derived, checked numerically

Nous avons résolu l'épreuve nous-mêmes : téléchargé les données, mené les expériences et mesuré le score ci-dessous. L'énoncé et les données appartiennent à leurs organisateurs. Cette correction est rédigée en anglais.

The problem in brief

The theory section of the 2025 Indian AI Olympiad (Stage 2) has three problems of 8 points each.

  1. Exam scores as random variables. A student's score XX and an AI assistant's score YY are correlated Gaussians, UU is a uniform adjustment and VV a discrete triangular bonus. You must reason about the distributions of their combinations.
  2. Debt resolution on a graph. A person in debt bb receives bb from each of their kk friends, which leaves them with b(k−1)b(k-1). Solve a chain example, prove that chains and trees can always be cleared when the total is non-negative, and find a counterexample on complete graphs.
  3. Social-media response times. Model them with Gamma distributions: maximum-likelihood estimates, whether K-means suits the data, a Bayes classifier between two groups, and the meaning of the Gamma parameters.

How you are scored

Answers are typed into a form. Numeric answers must match the key (integers rounded down, decimals to two places), and justifications earn separate points; expressions are graded generously. No answer key is published, so all answers below are derived here and checked numerically in solution.py. Throughout we assume that UU and VV are independent of each other and of (X,Y)(X, Y), and that (X,Y)(X, Y) is jointly Gaussian; the statement only says "correlated".

The key idea

  • Problem 1: variances add over independent terms, with a 2ρσXσY2\rho\sigma_X\sigma_Y correction for correlated ones; adding a discrete variable to a Gaussian gives a finite Gaussian mixture.
  • Problem 2: total wealth is preserved by every donation. On trees, a leaf can be merged into its neighbour: induction on the number of nodes. On complete graphs, pairwise differences modulo nn are invariant, which blocks some configurations forever.
  • Problem 3: a Bayes decision boundary is where log⁡pG+log⁡fG(x)=log⁡pT+log⁡fT(x)\log p_G + \log f_G(x) = \log p_T + \log f_T(x). For Gamma densities this is linear in xx and in log⁡x\log x, so it is a straight line exactly when the shapes are equal.

The full solution

Problem 1: score distributions

Q1(a). The ideal family. With NN problems scoring +1+1 (correct), 00 (skipped) or −0.5-0.5 (wrong), X=C−0.5 WX = C - 0.5\,W, where CC and WW count correct and wrong answers. If the problems are i.i.d. with probabilities (pc,ps,pw)(p_c, p_s, p_w), then (C,S,W)∼Multinomial(N;pc,ps,pw)(C, S, W) \sim \mathrm{Multinomial}(N; p_c, p_s, p_w), so XX is an affine function of a multinomial vector. It is discrete, bounded in [−N/2,N][-N/2, N], with mean N(pc−0.5pw)N(p_c - 0.5 p_w) and variance N(pc+0.25pw−(pc−0.5pw)2)N\big(p_c + 0.25 p_w - (p_c - 0.5 p_w)^2\big). With problems of unequal difficulty it becomes a sum of independent non-identical categorical variables (Poisson-multinomial type).

Q1(b). Is the Gaussian appropriate? As an approximation, yes, when NN is large and the probabilities are not extreme: XX is a sum of NN independent bounded terms, so the central limit theorem applies. But it is only an approximation: the true score is discrete and bounded, and it is skewed for very strong or very weak students (near the ceiling or the floor), where a Gaussian puts mass on impossible scores.

Q2(a). For jointly Gaussian (X,Y)(X, Y), any linear combination is Gaussian:

Z=X−Y∼N(μX−μY, σX2+σY2−2ρ σXσY).Z = X - Y \sim \mathcal N\big(\mu_X - \mu_Y,\ \sigma_X^2 + \sigma_Y^2 - 2\rho\,\sigma_X\sigma_Y\big).

Q2(b). Replace σX2\sigma_X^2 by σX2/α\sigma_X^2/\alpha and σY2\sigma_Y^2 by ασY2\alpha\sigma_Y^2, with ρ\rho unchanged. The standard deviations become σX/α\sigma_X/\sqrt\alpha and α σY\sqrt\alpha\,\sigma_Y, so their product, and hence the covariance term, does not change. The variance of ZZ is unchanged if and only if

σX2α+ασY2=σX2+σY2  ⟺  (1−α)(σX2α−σY2)=0,\frac{\sigma_X^2}{\alpha} + \alpha\sigma_Y^2 = \sigma_X^2 + \sigma_Y^2 \iff (1 - \alpha)\left(\frac{\sigma_X^2}{\alpha} - \sigma_Y^2\right) = 0,

that is α=1\alpha = 1 (no change) or α=σX2/σY2\alpha = \sigma_X^2/\sigma_Y^2 (the two variances swap). It does not depend on ρ\rho, because the term −2ρσXσY-2\rho\sigma_X\sigma_Y is the same before and after.

Q2(c).

Var⁡(3X−2Y)=9σX2+4σY2−12ρσXσY=36+64−76.8=23.2\operatorname{Var}(3X - 2Y) = 9\sigma_X^2 + 4\sigma_Y^2 - 12\rho\sigma_X\sigma_Y = 36 + 64 - 76.8 = \mathbf{23.2}

(23 in the integer format). The means play no role; a simulation of 10610^6 draws gives 23.23.

Q3(a). X+UX + U. The convolution of the Gaussian density with the uniform density on [a−T,a+T][a - T, a + T] gives

fX+U(s)=12T[Φ ⁣(s−μX−a+TσX)−Φ ⁣(s−μX−a−TσX)],f_{X+U}(s) = \frac{1}{2T}\left[\Phi\!\left(\frac{s - \mu_X - a + T}{\sigma_X}\right) - \Phi\!\left(\frac{s - \mu_X - a - T}{\sigma_X}\right)\right],

a "smoothed box", with mean μX+a\mu_X + a and variance σX2+T2/3\sigma_X^2 + T^2/3 (the uniform on an interval of length 2T2T has variance (2T)2/12(2T)^2/12).

Q3(b). Increasing TT leaves the mean μX+a\mu_X + a unchanged and increases the variance quadratically, by T2/3T^2/3; the density becomes flatter and wider.

Q3(c). X+VX + V. Write V=b+jV = b + j. The probabilities are wj=(D−∣j∣)/D2w_j = (D - |j|)/D^2 for ∣j∣≤D|j| \le D; the two end points have weight 0, and the weights sum to 1. Conditioning on VV:

fX+V(s)=∑j=−(D−1)D−1D−∣j∣D2⋅1σX φ ⁣(s−μX−b−jσX),f_{X+V}(s) = \sum_{j=-(D-1)}^{D-1} \frac{D - |j|}{D^2}\cdot\frac{1}{\sigma_X}\,\varphi\!\left(\frac{s - \mu_X - b - j}{\sigma_X}\right),

with mean μX+b\mu_X + b and variance σX2+D2−16\sigma_X^2 + \frac{D^2 - 1}{6}, because

Var⁡(V)=2D2∑j=1D−1j2(D−j)=D2−16.\operatorname{Var}(V) = \frac{2}{D^2}\sum_{j=1}^{D-1} j^2 (D - j) = \frac{D^2 - 1}{6}.

Q3(d). It is a finite mixture of Gaussians: 2D−12D - 1 components with the same variance σX2\sigma_X^2, means μX+b+j\mu_X + b + j and triangular weights. It is a single Gaussian only in the degenerate case D=1D = 1, where V=bV = b is constant.

Q3(e). By independence, Var⁡(U+V)=T23+D2−16\operatorname{Var}(U + V) = \dfrac{T^2}{3} + \dfrac{D^2 - 1}{6} (simulated: 4.326 against 4.333 for T=3T = 3, D=3D = 3).

Q4. Final score with a=b=0a = b = 0. Let m=μX−μYm = \mu_X - \mu_Y and σZ2=σX2+σY2−2ρσXσY\sigma_Z^2 = \sigma_X^2 + \sigma_Y^2 - 2\rho\sigma_X\sigma_Y. Combining the two previous results, S=X−Y+U+VS = X - Y + U + V is a triangular mixture of "smoothed boxes":

fS(s)=∑∣j∣<DD−∣j∣2TD2[Φ ⁣(s−m−j+TσZ)−Φ ⁣(s−m−j−TσZ)],f_S(s) = \sum_{|j| < D} \frac{D - |j|}{2T D^2}\left[\Phi\!\left(\frac{s - m - j + T}{\sigma_Z}\right) - \Phi\!\left(\frac{s - m - j - T}{\sigma_Z}\right)\right],

with mean mm and variance σZ2+T2/3+(D2−1)/6\sigma_Z^2 + T^2/3 + (D^2 - 1)/6.

Problem 2: debt resolution

A donation at XX (debt bb, kk friends) adds kbkb to XX and removes bb from each friend, so the total is invariant. A configuration with negative total can therefore never be cleared, which is why the statement requires total ≥0\ge 0.

Q1. The chain (+3,−4,−2,+5,−1)(+3, -4, -2, +5, -1) (Anu, Ben, Cam, Dan, Eva), total +1+1. A breadth-first search over all legal sequences gives a shortest solution of 8 donations:

donation atAnuBenCamDanEva
start3-4-25-1
Ben-14-65-1
Anu03-65-1
Cam0-36-1-1
Ben-333-1-1
Anu003-1-1
Eva003-20
Dan0012-2
Eva00100

Q2 and Q3. Chains and trees. A chain is a tree, so we prove the tree statement; for a chain, "leaf" simply means "end node". The proof is by induction on nn, for any configuration with total s≥0s \ge 0.

Base case. For n=1n = 1, the single value equals s≥0s \ge 0: nothing to do.

Inductive step. Take a leaf LL (it exists for n≥2n \ge 2) with its unique neighbour PP, and let T′=T∖{L}T' = T \setminus \{L\}, again a tree, with n−1n - 1 nodes. In T′T', give PP the merged value P′=P+LP' = P + L; the total of T′T' is still ss.

Case A: L≤0L \le 0. If L<0L < 0, donate at LL: LL becomes 00 and PP loses ∣L∣|L|, so now L=0L = 0 and P=P′P = P'. By induction there is a finite sequence clearing T′T'. Replay it in TT:

  • a donation at a node other than PP is identical in TT and T′T' (the leaf is not its friend);
  • a donation at P′P' (debt b′b', k′k' friends in T′T') becomes two moves in TT. First donate at PP, whose debt is also b′b': PP gets k′b′k'b', its T′T'-friends lose b′b' each, and LL goes to −b′-b'. Then donate at LL: LL returns to 00 and PP loses b′b'. The result is P=(k′−1)b′P = (k' - 1)b', exactly as in T′T', with L=0L = 0.

So the replay keeps L=0L = 0 and P=P′P = P'; when T′T' is cleared, all of TT is.

Case B: L=c>0L = c > 0. Replay the sequence of T′T' as before; while PP never donates, LL stays equal to cc and P=P′−cP = P' - c. Two events can interrupt the replay.

  • T′T' asks P′P' to donate. Do the same two moves as in case A (PP's debt is now b′+cb' + c). Afterwards L=0L = 0: the new configuration of T′T' is not the planned one, but it still has total s≥0s \ge 0. We restart in case A, which terminates.
  • The sequence of T′T' ends with P′≥0P' \ge 0 but P=P′−c<0P = P' - c < 0. Then donate at PP: its debt b=c−P′b = c - P' satisfies 0<b≤c0 < b \le c, so LL becomes c−b=P′≥0c - b = P' \ge 0, which is strictly smaller than cc. We restart case B with this smaller reserve.

The reserve cc is a non-negative integer that strictly decreases at each restart of the second kind, and a restart of the first kind leads to case A. So after finitely many rounds everything is debt-free. ■\blacksquare

solution.py implements exactly this recursive procedure and clears all debts on 3,000 random trees (up to 9 nodes, values in [−7,7][-7, 7], total ≥0\ge 0).

Q4. A counterexample on KnK_n. In the complete graph, every node is everyone's friend. A donation at XX with debt bb subtracts bb from every node and adds nbnb to XX. Hence every pairwise difference vi−vjv_i - v_j is invariant modulo nn. Take

v=(−1, 0, 1, 2, …, n−2),s=(n−1)(n−2)2−1≥0(n≥3).v = (-1,\ 0,\ 1,\ 2,\ \dots,\ n - 2), \qquad s = \frac{(n-1)(n-2)}{2} - 1 \ge 0 \quad (n \ge 3).

The nn values are pairwise distinct modulo nn, and they stay so forever. A debt-free configuration with nn distinct residues has total at least 0+1+⋯+(n−1)=n(n−1)20 + 1 + \dots + (n - 1) = \frac{n(n-1)}{2}, which is larger than ss. Since the total is preserved, the debts can never all be cleared, whatever the order of donations. An exhaustive search confirms that no sequence clears (−1,0,1)(-1, 0, 1) on K3K_3, (−1,0,1,2)(-1, 0, 1, 2) on K4K_4, or (−1,0,…,3)(-1, 0, \dots, 3) on K5K_5. The idea is the same as the "genus" of the graph in chip-firing: trees have none, complete graphs have cycles that create obstructions.

Problem 3: response times

Q1(a). Exponential case (k=1k = 1).

ℓ(θ)=−nlog⁡θ−1θ∑ixi,ℓ′(θ)=−nθ+∑xiθ2=0⇒θ^=xˉ,\ell(\theta) = -n\log\theta - \frac{1}{\theta}\sum_i x_i, \qquad \ell'(\theta) = -\frac{n}{\theta} + \frac{\sum x_i}{\theta^2} = 0 \Rightarrow \hat\theta = \bar x,

the sample mean of the response times. It is a maximum, since ℓ′′(θ^)=−n/xˉ2<0\ell''(\hat\theta) = -n/\bar x^2 < 0.

Q1(b). Both parameters free. The log-likelihood is

ℓ(k,θ)=(k−1)∑ilog⁡xi−1θ∑ixi−nklog⁡θ−nlog⁡Γ(k).\ell(k, \theta) = (k - 1)\sum_i \log x_i - \frac{1}{\theta}\sum_i x_i - nk\log\theta - n\log\Gamma(k).

Setting ∂ℓ/∂θ=0\partial\ell/\partial\theta = 0 gives θ^=xˉ/k\hat\theta = \bar x / k. Substituting into ∂ℓ/∂k=0\partial\ell/\partial k = 0, i.e. ∑log⁡xi−nlog⁡θ−nψ(k)=0\sum\log x_i - n\log\theta - n\psi(k) = 0, gives

log⁡k^−ψ(k^)=log⁡xˉ−log⁡x‾,\log\hat k - \psi(\hat k) = \log\bar x - \overline{\log x},

where ψ\psi is the digamma function and log⁡x‾\overline{\log x} the mean of log⁡xi\log x_i. There is no closed form, but the left side decreases strictly from +∞+\infty to 00, so there is a unique root (Newton's method; a good start is k0≈3−s+(s−3)2+24s12sk_0 \approx \frac{3 - s + \sqrt{(s-3)^2 + 24 s}}{12 s}, with ss the right side). The right side is ≥0\ge 0 by Jensen's inequality. Then θ^=xˉ/k^\hat\theta = \bar x/\hat k. On a simulated Gamma(2.5, 3) sample this recovers k^=2.497\hat k = 2.497, θ^=3.005\hat\theta = 3.005.

Q2(a). Does vanilla K-means converge? Yes, to a local minimum (a fixed point) in a finite number of steps. Neither the assignment step nor the update step increases JJ, and there are finitely many partitions, so with a fixed tie-breaking rule no partition repeats. The local minimum need not be global and depends on the initialisation (an empty cluster must be handled, for example by re-seeding it).

Q2(b). Should it be modified? Yes. Response times are positive and right-skewed, and the groups differ mainly in scale: GenX times are larger and more spread, since the variance kθ2k\theta^2 grows with θ\theta. Squared Euclidean distance assumes round clusters of similar spread, so the slow, dispersed group gets split while the fast groups merge. Reasonable fixes:

  • log-transform the times (a scale change becomes a shift, and skewness drops), and standardise the two apps;
  • use a Bregman divergence matched to the Gamma family. For a fixed shape this is the Itakura-Saito divergence d(x,c)=xc−log⁡xc−1d(x, c) = \frac{x}{c} - \log\frac{x}{c} - 1, and the centroid is still the mean (Banerjee et al., 2005);
  • or, best, fit a mixture of Gamma distributions with EM (soft assignments, a (k,θ)(k, \theta) pair and a prior per group), which is exactly the model used in Q3.

Q3(a). Teen or GenX? Use the Bayes rule. Treat the two apps as independent given the group (the product form of the multivariate Gamma above), with log-density

log⁡fg(x)=∑a∈{W,I}[(ka,g−1)log⁡xa−xaθa,g−ka,glog⁡θa,g−log⁡Γ(ka,g)].\log f_g(x) = \sum_{a \in \{W, I\}} \Big[(k_{a,g} - 1)\log x_a - \frac{x_a}{\theta_{a,g}} - k_{a,g}\log\theta_{a,g} - \log\Gamma(k_{a,g})\Big].

Predict GenX when log⁡pG+log⁡fG(x)>log⁡pT+log⁡fT(x)\log p_G + \log f_G(x) > \log p_T + \log f_T(x). The boundary is

∑a[(ka,G−ka,T)log⁡xa−(1θa,G−1θa,T)xa]+C=0,\sum_{a} \Big[(k_{a,G} - k_{a,T})\log x_a - \Big(\tfrac{1}{\theta_{a,G}} - \tfrac{1}{\theta_{a,T}}\Big)x_a\Big] + C = 0,

where C=log⁡pGpT+∑a[ka,Tlog⁡θa,T+log⁡Γ(ka,T)−ka,Glog⁡θa,G−log⁡Γ(ka,G)]C = \log\frac{p_G}{p_T} + \sum_a\big[k_{a,T}\log\theta_{a,T} + \log\Gamma(k_{a,T}) - k_{a,G}\log\theta_{a,G} - \log\Gamma(k_{a,G})\big].

Q3(b). All shapes equal to 1. The log⁡x\log x terms vanish and the boundary ∑a(1/θa,T−1/θa,G) xa=const\sum_a (1/\theta_{a,T} - 1/\theta_{a,G})\,x_a = \text{const} is a straight line. Equal priors give one particular line; unequal priors only change the constant by log⁡(pG/pT)\log(p_G/p_T), which yields a parallel line shifted towards the less likely group.

Q3(c). Equal priors, different shapes. The log⁡xa\log x_a terms remain, so the boundary is curved (a level set of a sum of terms αlog⁡x−βx\alpha\log x - \beta x). When the GenX group has both the larger shape and the larger rate (α,β>0\alpha, \beta > 0), each term is concave with a maximum at x=α/βx = \alpha/\beta, and the GenX region becomes a bounded convex set enclosed by a closed curve.

Bayes boundaries between Teen and GenX for Gamma response times. Left: exponential case, straight lines, and unequal priors only shift them. Right: different shapes give a curved boundary.

Q4(a). Since the mean is kθk\theta and the variance kθ2k\theta^2, "much longer" WhatsApp replies for GenX mean kW,GθW,G≫kW,TθW,Tk_{W,G}\theta_{W,G} \gg k_{W,T}\theta_{W,T}. The natural reading is a larger scale θW,G>θW,T\theta_{W,G} > \theta_{W,T} (slower and more spread out), with comparable shapes; a larger kW,Gk_{W,G} would also raise the mean, but it would make the GenX times relatively more regular.

Q4(b). On Instagram, Teens are faster (smaller mean kθk\theta) and more consistent. The coefficient of variation of a Gamma is 1/k1/\sqrt k, so more consistency means a larger shape kI,T>kW,Tk_{I,T} > k_{W,T}, and a much smaller scale θI,T≪θW,T\theta_{I,T} \ll \theta_{W,T} to bring the mean down to seconds or minutes. On WhatsApp the Teen distribution is closer to exponential (k≈1k \approx 1, coefficient of variation near 1, heavy right tail) with a large θ\theta.

Results

questionanswer
P1 Q1multinomial (affine), Gaussian only as a large-NN approximation
P1 Q2Z∼N(μX−μY,σX2+σY2−2ρσXσY)Z \sim \mathcal N(\mu_X - \mu_Y, \sigma_X^2 + \sigma_Y^2 - 2\rho\sigma_X\sigma_Y); unchanged iff α∈{1,σX2/σY2}\alpha \in \{1, \sigma_X^2/\sigma_Y^2\}, independent of ρ\rho; Var⁡W=23.2\operatorname{Var} W = 23.2
P1 Q3X+UX + U: Gaussian convolved with a uniform, variance +T2/3+T^2/3; X+VX + V: mixture of 2D−12D - 1 Gaussians, variance +(D2−1)/6+(D^2 - 1)/6; Var⁡(U+V)=T2/3+(D2−1)/6\operatorname{Var}(U + V) = T^2/3 + (D^2 - 1)/6
P1 Q4triangular mixture of smoothed boxes, mean μX−μY\mu_X - \mu_Y, variance σZ2+T2/3+(D2−1)/6\sigma_Z^2 + T^2/3 + (D^2 - 1)/6
P2 Q1Ben, Anu, Cam, Ben, Anu, Eva, Dan, Eva (8 donations, final (0,0,1,0,0)(0, 0, 1, 0, 0))
P2 Q2-Q3induction by merging a leaf into its neighbour (verified on 3,000 random trees)
P2 Q4(−1,0,1,…,n−2)(-1, 0, 1, \dots, n - 2): residues mod nn are invariant
P3 Q1θ^=xˉ\hat\theta = \bar x; log⁡k^−ψ(k^)=log⁡xˉ−log⁡x‾\log\hat k - \psi(\hat k) = \log\bar x - \overline{\log x}, θ^=xˉ/k^\hat\theta = \bar x/\hat k
P3 Q2converges to a local minimum; modify (log scale, Itakura-Saito, or Gamma-mixture EM)
P3 Q3Bayes log-ratio boundary; linear when k=1k = 1 (priors shift it), curved otherwise
P3 Q4GenX on WhatsApp: larger θ\theta; Teens on Instagram: larger kk, much smaller θ\theta

What to remember

  • Look for invariants first. Total wealth (always) and differences modulo nn (on KnK_n) instantly tell you what is impossible.
  • Induction on trees: remove a leaf. Merging a leaf into its neighbour, and simulating each of the neighbour's moves with one extra move at the leaf, is a reusable proof template.
  • Correlated sums need the covariance term, and rescaling two variances by α\alpha and 1/α1/\alpha leaves their covariance unchanged.
  • Discrete plus Gaussian gives a Gaussian mixture; uniform plus Gaussian gives a smoothed box. Means and variances simply add for independent terms.
  • Match the algorithm to the data's geometry. Skewed, scale-driven data calls for log transforms, the right divergence or a generative mixture, rather than plain Euclidean K-means; the Bayes boundary between exponential families is linear in their sufficient statistics (xx and log⁡x\log x for the Gamma).
Code complet (solution.py)
"""Numerical checks for INAIO 2025 Stage 2, theory problems 1-3."""
import random, itertools, math
from collections import deque
import numpy as np
from scipy.special import digamma
from scipy.optimize import brentq
rng = np.random.default_rng(0)

# ---------------- Problem 1 ----------------
sx, sy, r = 2.0, 4.0, 0.8
print("P1 Q2c  Var(3X-2Y) =", 9 * sx**2 + 4 * sy**2 - 2 * 3 * 2 * r * sx * sy)
cov = [[sx**2, r * sx * sy], [r * sx * sy, sy**2]]
XY = rng.multivariate_normal([0, 1], cov, size=1_000_000)
print("        simulated   =", round(np.var(3 * XY[:, 0] - 2 * XY[:, 1]), 2))
a = 2.0
print("P1 Q2b  alpha that keeps Var(X-Y) for sx=2, sy=4:", sx**2 / sy**2,
      "check:", sx**2 / (sx**2 / sy**2) + (sx**2 / sy**2) * sy**2 - 2 * r * sx * sy, "vs", sx**2 + sy**2 - 2 * r * sx * sy)
for D in (1, 2, 3, 5):
    k = np.arange(-D, D + 1); p = (D - np.abs(k)) / D**2
    print(f"P1 Q3  D={D}: sum P={p.sum():.3f}  Var V={np.sum(p * k**2):.4f}  (D^2-1)/6={(D * D - 1) / 6:.4f}")
T, D = 3.0, 3
U = rng.uniform(-T, T, 1_000_000); k = np.arange(-D + 1, D); V = rng.choice(k, 1_000_000, p=(D - np.abs(k)) / D**2)
print("P1 Q3e  Var(U+V) sim", round(np.var(U + V), 3), "formula", T * T / 3 + (D * D - 1) / 6)


# ---------------- Problem 2 ----------------
def donate(v, adj, x):
    v = list(v); b = -v[x]; assert b > 0
    for y in adj[x]:
        v[y] -= b
    v[x] = b * (len(adj[x]) - 1)
    return v


chain = [[1], [0, 2], [1, 3], [2, 4], [3]]
names = ["Anu", "Ben", "Cam", "Dan", "Eva"]; v = [3, -4, -2, 5, -1]
for x in [1, 0, 2, 1, 0, 4, 3, 4]:      # shortest sequence (breadth-first search)
    v = donate(v, chain, x); print("P2 Q1 ", names[x].ljust(4), v)


def solve_tree(vals, adj, nodes):
    """Constructive proof of Q2/Q3: returns a list of donations clearing all debts (sum >= 0).
    Works on the sub-tree `nodes`; values are modified in place."""
    seq = []
    def act(x):
        nonlocal vals
        b = -vals[x]
        for y in adj[x]:
            if y in nodes:
                vals[y] -= b
        vals[x] = b * (sum(1 for y in adj[x] if y in nodes) - 1); seq.append(x)
    if len(nodes) == 1:
        return seq
    L = next(u for u in nodes if sum(1 for y in adj[u] if y in nodes) == 1)
    P = next(y for y in adj[L] if y in nodes)
    rest = nodes - {L}
    if vals[L] < 0:
        act(L)                                   # case A start: leaf -> 0
    while True:
        # recursive plan on the reduced tree where P carries P + L
        sub = dict(vals); sub[P] = vals[P] + vals[L]
        plan = solve_tree(sub, adj, rest)
        restart = False
        for x in plan:
            if x == P:
                c = vals[L]
                act(P)                           # P borrows; the leaf goes to -(debt of P')
                if vals[L] < 0:
                    act(L)                       # leaf borrows back: now leaf = 0
                if c > 0:
                    restart = True; break        # configuration changed: re-plan (now case A)
            else:
                act(x)
        if restart:
            continue
        if vals[P] < 0:                          # only possible while the leaf holds c > 0
            act(P)
            continue                             # leaf reserve strictly decreased: re-plan
        return seq


random.seed(1); ok = 0
for t in range(3000):
    n = random.randint(1, 9); adj = [[] for _ in range(n)]
    for i in range(1, n):
        j = random.randrange(i); adj[i].append(j); adj[j].append(i)
    while True:
        vals = {i: random.randint(-7, 7) for i in range(n)}
        if sum(vals.values()) >= 0:
            break
    s0 = sum(vals.values()); solve_tree(vals, adj, set(range(n)))
    ok += min(vals.values()) >= 0 and sum(vals.values()) == s0
print("P2 Q3  constructive proof cleared all debts on", ok, "of 3000 random trees")


def reachable_ok(v0, n, bound=40):
    adj = [[j for j in range(n) if j != i] for i in range(n)]
    seen = {tuple(v0)}; q = deque([tuple(v0)])
    while q:
        v = q.popleft()
        if min(v) >= 0:
            return True
        for x in range(n):
            if v[x] < 0:
                w = tuple(donate(v, adj, x))
                if w not in seen and max(map(abs, w)) <= bound:
                    seen.add(w); q.append(w)
    return False


for n in (3, 4, 5):
    v0 = [-1] + list(range(n - 1))
    print(f"P2 Q4  K_{n} start {v0} (sum {sum(v0)}): debt-free reachable? {reachable_ok(v0, n)}")

# ---------------- Problem 3 ----------------
x = rng.gamma(shape=2.5, scale=3.0, size=200_000)
s = np.log(x.mean()) - np.log(x).mean()
k_hat = brentq(lambda k: np.log(k) - digamma(k) - s, 1e-3, 1e3)
print(f"P3 Q1b  MLE on Gamma(2.5, 3) sample: k={k_hat:.3f}, theta=xbar/k={x.mean() / k_hat:.3f}")
print("P3 Q1a  exponential MLE theta = mean:", round(rng.exponential(4.0, 200_000).mean(), 3))