Windows to Infinity
Category: Programming
Server: play.scriptsorcerers.xyz:10406
Flag: scriptCTF{i_10v3_m4_w1nd0wwwwwwww5_058aa4930d6d}
TL;DR
We connect to a TCP service that prints N = 1,000,000 integers (each in [0, 100000]). For a fixed window size M = N/2 = 500,000, we must output M+1 values per round for 8 sliding-window tasks: sum, xor, mean (floor), median (lower), mode (max value on ties), mex, distinct count, and sum of pairwise GCDs. We solved each round in streaming/online fashion with O(1) or O(log V) amortized updates and careful buffering for I/O.
Challenge Overview
The service:
- Prints 1,000,000 numbers once.
- For each of 8 rounds, reads one line of our answers (M+1 integers).
- Verifies and continues; on any mismatch it prints
uh ohand closes.
- On success through Round 8, prints the flag.
Key parameters:
N = 1,000,000
M = N/2 = 500,000
- Value domain:
0 … 100,000(important for our data structures)
Approach & Data Structures (Round by Round)
We implemented a single C++ client that:
- Opens a TCP socket to the host/port.
- Streams in all
Nnumbers (fast parser).
- Streams out answers per round (custom buffered writer).
Round 1 — Sums of each window
- Method: Prefix sums:
pref[i+1] = pref[i] + a[i].
- Window sum:
sum[i] = pref[i+M] - pref[i].
- Complexity: O(N).
Round 2 — XORs of each window
- Method: Prefix XORs:
px[i+1] = px[i] ^ a[i].
- Window xor:
xor[i] = px[i+M] ^ px[i].
- Complexity: O(N).
Round 3 — Means (floored)
- Method: Reuse sums; output
sum[i] / M.
- Complexity: O(N).
Round 4 — Median (lower median for even M)
- Spec nuance: The problem defines median via
a[floor(n/2)]. For even M this is the lower median.
- Method: Fenwick/BIT over value domain
[0..100000]tracking frequencies.- Update on slide:
add(a[i+M]),remove(a[i]).
- Query k-th (0-based) with
k = (M-1)/2.
- Update on slide:
- Complexity: O((N + #(slides)) · log V) ≈ O(N log 1e5).
Round 5 — Mode (break ties toward larger value)
- Method: Frequency array
cnt[v], plus a lazy heap of pairs(-count, -value):- On add/remove, push the new
(−cnt[v], −v)into a min-heap.
- When querying, pop until the top matches current
cnt(lazy deletion).
- Tie-breaking by larger value is handled by
value.
- On add/remove, push the new
- Complexity: Amortized O(log V) per update/query.
Round 6 — Mex (minimum excluded)
- Method: Maintain
cnt[v]and a min-heapmissingseeded with allv≥0.- On add:
cnt[v]++.
- On remove: if
cnt[v]becomes 0, pushvback intomissing.
- Query: pop from
missinguntil top hascnt[top]==0.
- On add:
- Complexity: Amortized O(log V).
Round 7 — # of Distinct Numbers
- Method:
cnt[v]and an integerdistinct. Increment when a count goes 0→1, decrement when 1→0.
- Complexity: O(1) per update.
Round 8 — Sum of pairwise GCDs in each window
We use a classic identity over positive integers:
∑i<jgcd(xi,xj) = ∑d≥1φ(d) (F[d]2)\sum_{i<j} \gcd(x_i, x_j) \;=\; \sum_{d\ge 1} \varphi(d)\,\binom{F[d]}{2}
i<j∑gcd(xi,xj)=d≥1∑φ(d)(2F[d])
where F[d] = number of elements divisible by d in the window, and φ is Euler’s totient.
Zero handling: gcd(0, x) = x and gcd(0,0)=0. We exclude zeros from F and add a term for zero–positive pairs:
answer = ∑d≥1φ(d) (F[d]2) + (#zeros)⋅∑xi>0xi\text{answer} \;=\; \sum_{d\ge1}\varphi(d)\,\binom{F[d]}{2} \;+\; (\#\text{zeros}) \cdot \sum_{x_i>0} x_i
answer=d≥1∑φ(d)(2F[d])+(#zeros)⋅xi>0∑xi
Implementation:
- Precompute
phi[d](linear sieve) and divisorsdivs[v]for allv≤100000.
- Maintain:
F[d]counts for alld.
Spos = \sum_d φ(d)*C2(F[d])incrementally:- On add v>0: for each
d | v,Spos += φ(d) * F[d], thenF[d]++.
- On remove v>0: first
F[d]--, thenSpos -= φ(d) * F[d].
- On add v>0: for each
zerosandsum_posfor the zero term.
Complexity: Each add/remove touches all divisors of v (≈ up to ~128 in worst/common cases). With 1e6 scale and buffered I/O it’s fine.
I/O & Engineering Notes
- Fast input: Manual socket
recv()with a simple digit parser (numbers separated by spaces/newlines).
- Fast output: Buffered line writer; flush per round.
- Domain-bounded DS: All value-indexed structures sized to
MAXV+1 = 100001.
- Pitfalls we hit (and fixed):
- A stray
}(and a pasted diff marker}) accidentally closedmain()early → compile errors & missingcliscope. Fix: remove the extra brace/line so the “tail read” remains insidemain.
- Median definition: the judge uses lower median for even
M. Settingk = (M-1)/2is required; using upper median caused mismatches.
- A stray
Build & Run
g++ -O3 -std=c++20 -march=native -pipe windows_to_infinity_solver.cpp -o solver
./solver play.scriptsorcerers.xyz 10406You should see the round banners, then the flag:
Round 1: Sums!
...
Round 8: Sum of pairwise GCD!
scriptCTF{i_10v3_m4_w1nd0wwwwwwww5_058aa4930d6d}Complexity Summary
- Rounds 1–3: O(N)
- Median: O((N) log V)
- Mode: Amortized O(N log V)
- Mex: Amortized O(N log V)
- Distinct: O(N)
- Pairwise GCD sum: ~O(N · avg_divisors(v))
With V=100001, all rounds finish comfortably when paired with fast I/O.
Lessons Learned
- Clarify median definition (lower vs upper) early; it’s a common off-by-one trap.
- For large sliding windows, data structures on the value domain (BIT, heaps with lazy deletion, divisor/phi precomp) are the way to go.
- Zero handling in number-theory aggregates matters (gcd term).
- Avoid leaking debug/diff artifacts into source (the
}line).