← Back to Writeups
CUSTOMEasyCoding

Granary Seal

XESXOR9/2/20263 min read
#coding#custom#easy#role_specific_set_membership#linear_scan#byte_token_parsing

Granary Seal

Platform: CACTF2026 | Category: Coding | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-07-27 | Status: Solved Techniques: role_specific_set_membership, linear_scan, byte_token_parsing

Summary

Task: Given three role-specific custody rolls and candidate ration orders, count the orders whose names are authorized for their positions. Solution: Store each roll in a hash set and test every order with three corresponding membership checks.

Granary Seal — CACTF2026

Description

The challenge provides three role-specific custody rolls (clerks, countersigners, couriers), followed by N ration orders, each containing clerk/countersigner/courier names. Count orders where every name occurs in the custody roll for its corresponding role.

The service is a web coding judge. It presents three independent lists of authorized names and then a sequence of candidate orders. The goal is to count orders in which all three names are authorized for their exact roles. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Analysis

The wording emphasizes that every person must appear “where it belongs.” Therefore, the three positions have distinct membership conditions. Let:

  • clerks be the set of names on the clerk roll;
  • countersigners be the set of names on the countersigner roll;
  • couriers be the set of names on the courier roll.

For an order (clerk, countersigner, courier), validity is exactly:

text
clerk in clerks
AND countersigner in countersigners
AND courier in couriers

The rolls are independent. The task does not provide or require a collection of previously observed complete triples, so whole-triple historical matching would add a condition absent from the specification. Cross-role membership is also insufficient: a clerk name appearing only on another role's roll does not authorize that name as a clerk. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Hash sets make each expected membership test constant time. Reading the input as whitespace-separated byte tokens avoids unnecessary decoding and handles names efficiently.

Algorithm

  1. Read the clerk count and insert those names into a set.
  2. Repeat for countersigners and couriers.
  3. Read the number of candidate orders.
  4. For each order, read its three names in role order.
  5. Increment the answer only when all three corresponding set-membership tests succeed.
  6. Print the answer.

Correctness Argument

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Consider any candidate order with names (c, s, r).

  • If the algorithm counts the order, then c is in the clerk roll, s is in the countersigner roll, and r is in the courier roll because the increment occurs only when all three checks are true. Thus the order satisfies every required role condition and is valid.
  • If the order is valid, each of its names belongs to the custody roll for its corresponding role. All three checks are therefore true, so the algorithm increments the count for that order. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Hence the algorithm counts every valid order and no invalid order. After all candidates are processed, the printed total is the required answer.

Complexity

Let C, CS, and R be the sizes of the clerk, countersigner, and courier rolls, and let N be the number of candidate orders.

  • Expected time: O(C + CS + R + N)
  • Space: O(C + CS + R)

Solution

python
import sys


def solve() -> None:
    tokens = iter(sys.stdin.buffer.read().split())

    clerks = {next(tokens) for _ in range(int(next(tokens)))}
    countersigners = {next(tokens) for _ in range(int(next(tokens)))}
    couriers = {next(tokens) for _ in range(int(next(tokens)))}
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

    valid = 0
    for _ in range(int(next(tokens))):
        clerk, countersigner, courier = next(tokens), next(tokens), next(tokens)
        valid += (
            clerk in clerks
            and countersigner in countersigners
            and courier in couriers
        )

    print(valid)


if __name__ == "__main__":
    solve()

Submission and Result Evidence

The target identified itself as Werkzeug 3.1.3 on Python 3.14.0. GET / exposed the statement, while POST /run accepted JSON fields named code and language. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Submitting the solver produced a JSON response with challengeCompleted set to true, an empty result object, and the accepted flag value (redacted here). This confirms that independent role-specific membership is the complete validation rule used by the hidden tests. ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍