Sums

TL;DR

Compute n range-sum queries fast using a prefix-sum array, send back exactly n answers, one per line. Use buffered I/O to stay under the 10s server limit.

Flag: scriptCTF{1_w4n7_m0r3_5um5_c2b7a07c8079}


Challenge Overview

  • Category: Programming
  • Service: nc play.scriptsorcerers.xyz 10426
  • Goal: For each query [l, r] (0-indexed, inclusive), return sum(nums[l:r+1]).
  • Gotchas:
    • n = 123456 (large).
    • You must return exactly n lines of answers.
    • Time limit: 10 seconds (server-side).
    • The server independently computes the correct answers internally and checks your output line-by-line.

What the Server Does (Given Code Summary)

  1. Generates n=123456 random integers nums[i] in [0, 696969].
  1. Prints one giant line with all n numbers separated by spaces.
  1. Prints n lines of queries l r, inclusive, 0-indexed.
  1. Internally runs its own solver (./solve) to get the correct answers.
  1. Reads your n answers from stdin and compares with its own.
  1. If they match and total elapsed time ≤ 10s, prints the flag.

Implication: Our output must be correct and fast.


Strategy

Use a prefix-sum array pref where:

  • pref[0] = 0
  • pref[i+1] = pref[i] + nums[i]

Then each query sum is:

sum(nums[l:r+1]) = pref[r+1] - pref[l]

This makes each query O(1) after an O(n) preprocessing pass.


Reproduction: Step-by-Step

✅ 1) Prerequisites

  • Python 3.8+ (tested with CPython; PyPy works too)
  • Network access to the challenge host

✅ 2) Save the client script

Create solve_remote.py with the following:

#!/usr/bin/env python3
import socket

HOST = "play.scriptsorcerers.xyz"
PORT = 10426

def main():
    with socket.create_connection((HOST, PORT)) as sock:
        rf = sock.makefile("r", encoding="utf-8", newline="\n")
        wf = sock.makefile("w", encoding="utf-8", newline="\n")

        # 1) Read the big line of numbers
        nums_line = rf.readline()
        if not nums_line:
            return
        nums_strs = nums_line.strip().split()
        n = len(nums_strs)
        nums = list(map(int, nums_strs))

        # 2) Build prefix sums
        pref = [0] * (n + 1)
        s = 0
        for i, a in enumerate(nums, 1):
            s += a
            pref[i] = s

        # 3) Answer exactly n queries
        out = []
        append = out.append
        for _ in range(n):
            line = rf.readline()
            l_str, r_str = line.split()
            l = int(l_str); r = int(r_str)
            append(str(pref[r + 1] - pref[l]))

        # 4) Send all answers in one go (fewer syscalls)
        wf.write("\n".join(out) + "\n")
        wf.flush()

        # 5) Read remainder (flag or error)
        # Using read() to EOF ensures we don't miss the flag line.
        remainder = rf.read()
        if remainder:
            print(remainder, end="")

if __name__ == "__main__":
    main()

✅ 3) Run it

python3 solve_remote.py

If successful, you’ll see the flag:

scriptCTF{1_w4n7_m0r3_5um5_c2b7a07c8079}

Notes on Performance & Robustness

  • Why buffered I/O? Using makefile() and writing all answers at once reduces syscall overhead and comfortably fits within the 10s limit.
  • Complexity:
    • Prefix computation: O(n)
    • Query answers: O(n)
    • Total: O(n) time, O(n) memory
  • Integer safety: Python ints are arbitrary precision; sums won’t overflow.

Common Pitfalls

  • ❌ Printing answers one-by-one without buffering → slower I/O.
  • ❌ Forgetting the trailing newline at the end of the output.
  • ❌ Off-by-one on inclusive ranges; remember pref[r+1] - pref[l].
  • ❌ Assuming 1-indexed inputs; the problem is 0-indexed.

Lessons Learned

  • For large-scale query tasks, precompute and answer in O(1) per query.
  • For tight server timeouts, optimize I/O as much as the algorithm.
  • Always confirm indexing conventions and inclusivity of ranges.