Nice, indeed, I ended up using a set for the first part and then refactoring to a hash table for the second part.
Spoilers
#lang racket/base
(require
racket/set)
(define tachyon-manifold
(with-input-from-file "input.txt"
(lambda ()
(for/list ([line (in-lines)])
(string->list line)))))
(define manifold-edge
(sub1 (length (car tachyon-manifold))))
(define (beam-split beam)
(define row (car beam))
(define col (cdr beam))
(let* ([fork* (if (<= 0 (- col 1) manifold-edge)
(list (cons row (- col 1)))
(list))]
[fork* (if (<= 0 (+ col 1) manifold-edge)
(cons (cons row (+ col 1)) fork*)
fork*)])
fork*))
(define (beam-update beams beam from)
(hash-update
beams beam (lambda (count) (+ count (hash-ref beams from))) 0))
(define (beams-update beams from fork*)
(for/fold ([beams beams])
([beam (in-list fork*)])
(beam-update beams beam from)))
(define (beams-total beams)
(define last (sub1 (length tachyon-manifold)))
(for/sum ([(posn count) (in-immutable-hash beams)]
#:when (= last (car posn)))
count))
(define (propagate-beams manifold)
(for/fold ([beams (hash)]
[split 0]
#:result (values (beams-total beams) split))
([(cells row) (in-indexed (in-list manifold))])
(for/fold ([beams beams]
[split split]
#:result (values beams split))
([(cell col) (in-indexed (in-list cells))])
(define from (cons (- row 1) col))
(define beam (cons row col))
(case cell
[(#\S)
(values (hash-set beams beam 1) split)]
[(#\.)
(if (hash-has-key? beams from)
(values (beam-update beams beam from) split)
(values beams split))]
[(#\^)
(cond
[(hash-has-key? beams from)
(define -<* (beam-split beam))
(define -<N (if (null? -<*) 0 1))
(values (beams-update beams from -<*) (+ split -<N))]
[else
(values beams split)])]))))