0% found this document useful (0 votes)
2 views6 pages

AMS R01 Solution

The document outlines solutions for various algorithmic problems presented in the AMS Round 1 competition in March 2026. Each problem includes hints, a detailed solution approach, and a C++ code implementation. The problems cover topics such as expected returns, probability calculations, and optimal expected profits in a Bellman-optimal quote engine.

Uploaded by

Aggrt
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views6 pages

AMS R01 Solution

The document outlines solutions for various algorithmic problems presented in the AMS Round 1 competition in March 2026. Each problem includes hints, a detailed solution approach, and a C++ code implementation. The problems cover topics such as expected returns, probability calculations, and optimal expected profits in a Bellman-optimal quote engine.

Uploaded by

Aggrt
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Solution set

Algorithms & Mathematics Society AMS Round 1 | March 2026


THE GOLD STANDARD FOR QUANT-TRACK ENGINEERING TALENT [Link]

A. High-Frequency Expected Returns Time: 2s | Memory: 256 MB

Problem A:

Hint 1
Can you compute the expected profit for a single instrument i in isolation? Think about what each side (ask and bid) independently
contributes.

Hint 2
Since all fill events are independent across instruments and sides, linearity of expectation lets you sum contributions freely. For
instrument i, the ask contributes pi · si and the bid contributes qi · si , regardless of whether pi + qi ≤ 1.

Solution.
By linearity of expectation:
N
X
E[Profit] = si (pi + qi )
i=1

Simply read each triple and accumulate. O(N ) time, O(1) space.

C++ Solution // Author: tee_01

1 # include < bits / stdc ++. h >


2 using namespace std ;
3

4 int main () {
5 ios :: sync_with_stdio ( false ) ;
6 cin . tie ( nullptr ) ;
7

8 int N ; cin >> N ;


9 long double ans = 0.0 L ;
10

11 for ( int i = 0; i < N ; ++ i ) {


12 long double p , q , s ;
13 cin >> p >> q >> s ;
14 ans += s * ( p + q ) ;
15 }
16

17 cout << fixed << setprecision (6) << ( double ) ans << " \ n " ;
18 return 0;
19 }
B. Birthday Paradox Time: 2s | Memory: 256 MB

Problem B.
P
Given D days in a year and a threshold probability Q (with gcd(P, Q) = 1), find the minimum number of people N such that
P
P (collision) ≥ Q, where:

D × (D − 1) × · · · × (D − N + 1)
P (collision) = 1 − P (all different), P (all different) =
DN

Hint 1
P Q−P
Instead of tracking P (collision), think about its complement. You want P (all different) ≤ 1 − Q = Q . Can you multiply in one new
person at a time and check when this threshold is crossed?

Hint 2
Each new person n multiplies the running probability by D−n
D . Start with current_prob = 1.0 and keep multiplying until current_prob
≤ Q−P
Q . The answer is the number of multiplications performed. Since D ≤ 106 , this loop terminates quickly — the birthday paradox

guarantees N = O( D).

Solution.
Iterate n = 1, 2, . . ., maintaining:
D−n
current_prob ×=
D
Q−P

Stop when current_prob ≤ Q . Output n. By the birthday paradox, the loop runs at most O( D) steps, giving overall complexity

O(T D).

C++ Solution

1 # include < bits / stdc ++. h >


2 using namespace std ;
3 using ll = long long ;
4

5 void solve () {
6 ll D , P , Q ; cin >> D >> P >> Q ;
7 double target = ( double ) ( Q - P ) / Q ;
8

9 double current_prob = 1.0;


10 double invD = 1.0 / D ;
11 ll n = 1;
12

13 while ( current_prob > target ) {


14 current_prob *= ( D - n ) * invD ;
15 n ++;
16 }
17 cout << n << " \ n " ;
18 }
19

20 int main () {
21 ios_base :: sync_with_stdio ( false ) ;
22 cin . tie ( NULL ) ;
23 int t ; cin >> t ;
24 while (t - -) solve () ;
25 return 0;
26 }

C. Portfolio Drift Time: 2s | Memory: 256 MB


Hint 1
Since the same multiplicative shock is applied to all assets simultaneously each day, the expected total value after d days factors
P cleanly.
What is the expected multiplier for a single day? Can you express the answer as S0 × (single-day multiplier)d , where S0 = pi ?

Hint 2
The single-day expected multiplier is:

a 100 + x b 100 − y a(100 + x) + b(100 − y)


µ= · + · =
a+b 100 a+b 100 100(a + b)
9
Since the answer must be given modulo 109 + 7, division becomes modular inverse. Use Fermat’s little theorem: Q−1 ≡ Q10 +5

(mod 109 + 7). Then raise µ to the d-th power with fast exponentiation.

Solution Approach.
Pn
Step 1. Compute S0 = i=1 pi (mod 109 + 7).
Step 2. Compute the single-day expected multiplier as a fraction:

a(100 + x) + b(100 − y)
µ= (mod 109 + 7)
100(a + b)

using modular inverse for the denominator.


Step 3. The answer is S0 · µd (mod 109 + 7), computed via fast exponentiation in O(log d).
Overall complexity: O(n + log d).

C++ Solution

1 # include < bits / stdc ++. h >


2 using namespace std ;
3 using ll = long long ;
4 const ll MOD = 1 e9 + 7;
5

6 ll power ( ll base , ll exp ) {


7 ll res = 1; base %= MOD ;
8 while ( exp > 0) {
9 if ( exp & 1) res = res * base % MOD ;
10 base = base * base % MOD ;
11 exp > >= 1;
12 }
13 return res ;
14 }
15

16 ll modInverse ( ll n ) { return power (n , MOD - 2) ; }


17

18 void solve () {
19 ll n , d , a , b , x , y ;
20 cin >> n >> d >> a >> b >> x >> y ;
21

22 ll S0 = 0;
23 for ( int i = 0; i < n ; ++ i ) {
24 ll p ; cin >> p ;
25 S0 = ( S0 + p ) % MOD ;
26 }
27

28 ll numerator = ( a % MOD * ((100 + x ) % MOD ) % MOD


29 + b % MOD * ((100 - y + MOD ) % MOD ) % MOD ) % MOD ;
30 ll denominator = 100 % MOD * (( a + b ) % MOD ) % MOD ;
31

32 ll mu = numerator % MOD * modInverse ( denominator ) % MOD ;


33 ll ans = S0 * power ( mu , d ) % MOD ;
34 cout << ans << " \ n " ;
35 }
36

37 int main () {
38 ios_base :: sync_with_stdio ( false ) ;
39 cin . tie ( NULL ) ;
40 solve () ;
41 return 0;
42 }
D. Apex Duel Probability Time: 1s | Memory: 256 MB

Problem D.
A tournament of 2n nodes proceeds over n stages. At each stage k , the 2k surviving nodes are paired uniformly at random; the node
with the smaller dominance index always wins. Nodes 1 and 2 always survive until they meet. Find the probability that nodes 1 and 2
meet exclusively in Stage 1 (the final), expressed as an irreducible fraction p/q .

Hint 1
Since nodes 1 and 2 always beat everyone else, both are guaranteed to survive every stage. So the only question is: when do they get
paired? At stage k there are 2k nodes. What is the probability that two specific nodes are paired together?

Hint 2
With 2k nodes, the probability two specific nodes are paired is 2k1−1 . For nodes 1 and 2 to meet only in Stage 1, they must avoid each
other in every stage k = n, n − 1, . . . , 2, then meet in Stage 1 (probability 1). This gives a telescoping product — look for cancellation
across consecutive terms.

Solution Approach.
2k −2
The probability nodes 1 and 2 are not paired at stage k is 2k −1
. So:

n n n
Y 2k − 2 Y 2(2k−1 − 1) n−1
Y 2k−1 − 1
P = = = 2 ·
2k − 1 2k − 1 2k − 1
k=2 k=2 k=2

The product telescopes:


n
Y 2k−1 − 1 (21 − 1)(22 − 1) · · · (2n−1 − 1) 1
= = n
2k − 1 (22 − 1)(23 − 1) · · · (2n − 1) 2 −1
k=2

Therefore:
2n−1
P =
2n − 1
Since 2n−1 is even and 2n − 1 is odd, gcd(2n−1 , 2n − 1) = 1 — the fraction is already irreducible. For n ≤ 62, values fit in 64-bit integers
but use __int128 to safely compute 2n .

C++ Solution

1 # include < bits / stdc ++. h >


2 using namespace std ;
3 using ll = long long ;
4

5 int main () {
6 ios :: sync_with_stdio ( false ) ;
7 cin . tie ( nullptr ) ;
8

9 ll n ; cin >> n ;
10

11 __int128 num = ( __int128 ) 1 << ( n - 1) ;


12 __int128 den = (( __int128 ) 1 << n ) - 1;
13

14 ll g = __gcd (( ll ) num , ( ll ) den ) ;


15 num /= g ;
16 den /= g ;
17

18 cout << ( ll ) num << " / " << ( ll ) den << " \ n " ;
19 return 0;
20 }
E. Spread Extraction Valuation Engine (SEVE) Time: 2s | Memory: 256 MB

Problem E.
A Bellman-optimal quote engine operates in a liquidity regime with N equiprobable outcomes per cycle:

• Events 1 through N − 1: favourable fills — if current P&L is M , it becomes M + k after event k


• Event N : adverse selection event (ASE) — position liquidated at zero P&L; cycle terminates immediately

At each step the engine chooses to continue (risk the next event) or flatten (lock in current P&L), always following the Bellman-optimal
policy. Given N and Q queries, answer:

• Type 1 — Seat Fee: is the optimal expected P&L from M = 0 at least F ?


• Type 2 — State Valuation: given current mark-to-market P&L of M ticks, what is the maximum achievable expected P&L under the
optimal policy?

Hint 1
Let ev(M ) be the Bellman-optimal expected P&L from state M . The engine continues only if the expected gain from doing so exceeds
M (i.e. flattening). Note that once M ≥ T = N (N2−1) , the remaining events can add at most T ticks total, so flattening is always optimal.
What is ev(M ) for M ≥ T ?

Hint 2
For M ≥ T , ev(M ) = M (flatten immediately). For M < T , since the engine is Bellman-optimal and ev is non-decreasing, continuing
is always better — so:
N −1
1 X
ev(M ) = ev(M + k)
N
k=0

This is a sliding window average of N consecutive values. Fill backwards from M = T − 1 to M = 0, maintaining a running sum of the
window.

Solution Approach.

N (N −1)
Threshold. For M ≥ T = 2 , always flatten: ev(M ) = M .
Backward recurrence. For M < T :
N −1
1 X
ev(M ) = ev(M + k)
N
k=0

Initialise ev(M ) = M for all M ≥ T . Then iterate M = T − 1 down to 0, keeping a running sum over the window [ev(M ), . . . , ev(M +
N − 1)]. Each step: add ev(M ), subtract ev(M + N − 1), divide by N .
Queries. Type 1: compare ev(0) against F . Type 2: output M if M ≥ T , else ev(M ).
100×99
Complexity. O(T ) per test case with T ≤ 2 = 4950, then O(1) per query.
C++ Solution

1 # include < bits / stdc ++. h >


2 using namespace std ;
3 using ll = long long ;
4 using ld = long double ;
5

6 const int MAXN = 3000005;


7

8 int main () {
9 ios_base :: sync_with_stdio ( false ) ;
10 cin . tie ( NULL ) ;
11 cout << fixed << setprecision (2) ;
12

13 int t ; cin >> t ;


14 while (t - -) {
15 ll n , q ; cin >> n >> q ;
16 ll threshold = n * ( n - 1) / 2;
17

18 ll limit = min ( threshold , ( ll ) MAXN ) ;


19 vector < ld > ev ( limit + n + 100) ;
20

21 // Base case : ev ( M ) = M for M >= threshold


22 for ( ll i = limit ; i < limit + n + 50; i ++)
23 ev [ i ] = ( ld ) i ;

24

25 // Sliding window sum over [ ev ( limit ) , ... , ev ( limit +n -2) ]


26 ld current_sum = 0;
27 for ( int i = 0; i < n - 1; i ++)
28 current_sum += ev [ limit + i ];
29

30 // Fill backwards
31 for ( ll m = limit - 1; m >= 0; m - -) {
32 ev [ m ] = current_sum / n ;
33 current_sum += ev [ m ];
34 current_sum -= ev [ m + n - 1];
35 }
36

37 while (q - -) {
38 int type ; cin >> type ;
39 if ( type == 1) {
40 ll f ; cin >> f ;
41 cout << ( ev [0] >= ( ld ) f - 1e -9 L ? " YES " : " NO " ) << " \ n " ;
42 } else {
43 ll m ; cin >> m ;
44 if ( m >= threshold ) cout << m << " .00\ n " ;
45 else cout << ev [ m ] << " \ n " ;
46 }
47 }

48 }
49 return 0;
50 }

You might also like