8
ADD alpha
ADD beta
ADD gamma
GET chat_1
GET chat_2
GET chat_3
GET chat_4
GET chat_5gamma
gamma
gamma
gamma
gammaProblem contributed by @asdf
Implement a HashRing, a consistent hashing ring that routes chat sessions to servers.
Design a distributed chat load balancer using a consistent hashing ring — a circle of integers in the range [0, 2^32). Both servers and chats are assigned positions using a hash function. A chat is routed to the first server clockwise from its position on the ring, with wraparound.
This problem is divided into multiple parts. Each part builds on the previous one.
A hash function is provided — treat it as a black box:
int hash_fn(string key); // provided, do not implementTiebreak rule (applies to all parts): When scanning the ring clockwise, distinct physical servers are visited in clockwise order starting from the primary. If two ring positions collide on the same hash, ties are broken lexicographically by server_id. Test data will not rely on collision tiebreaks for correctness, but your implementation must be deterministic.
HashRing()add_server(server_id)hash_fn(server_id).remove_server(server_id)get_server(chat_id) -> strchat_id and returns the first server clockwise from that position on the ring.A single ring position per server leads to uneven load distribution. Extend your implementation to support virtual nodes: each physical server gets v positions on the ring at:
hash_fn(server_id + "#0"), hash_fn(server_id + "#1"), ..., hash_fn(server_id + "#v-1")Modify add_server and remove_server to handle virtual nodes. get_server behavior is unchanged — it still returns the physical server id.
add_server(server_id, v)v virtual nodes at the positions described above.add_server(server_id) from Part 1 is equivalent to add_server(server_id, 1) with the position computed from hash_fn(server_id) directly (no #0 suffix).remove_server(server_id)Extend routing to support affinity-based fallback. When looking up a chat, instead of always returning the primary ring server, scan up to k distinct physical servers clockwise (including the primary) and prefer servers that have previously handled this chat.
record_affinity(server_id, chat_id)server_id has an affinity for chat_id.get_server_v3(chat_id, k) -> strk distinct physical servers clockwise from chat_id's ring position (including the primary).k is greater than the number of distinct physical servers on the ring, the scan visits every server exactly once.chat_id, return the first such server in clockwise order.Affinities are tied to a server's identity. If a server is removed, its affinities are discarded; re-adding the same server_id later starts with no affinities.
Each server now tracks two memory tiers:
Replace affinity tracking from Part 3 with explicit memory state. When routing a chat_id, apply the following priority logic:
chat_id in VRAM, route there.chat_id in RAM, route there.k distinct physical servers clockwise (including the primary).chat_id in VRAM, route to the first one in clockwise order.chat_id in RAM, route to the first one in clockwise order.load_to_vram(server_id, chat_id)chat_id as loaded in VRAM on server_id.chat_id was in RAM on the same server, it is removed from RAM.load_to_ram(server_id, chat_id)chat_id as loaded in RAM on server_id.chat_id was in VRAM on the same server, it is removed from VRAM.A chat may exist in VRAM or RAM on a given server, but not both.
get_server_v4(chat_id, k) -> strchat_id using the priority logic above, scanning up to k distinct physical servers clockwise.Memory state, like affinity, is tied to a server's identity. If a server is removed, its VRAM and RAM contents are discarded; re-adding the same server_id later starts with empty memory.
All server_id and chat_id values consist of lowercase letters, digits, and underscores.
The ring is guaranteed to be non-empty at the time of any GET, GET3, or GET4 operation.
record_affinity, load_to_vram, and load_to_ram are only called with server_id values that currently exist on the ring.
You do not need to declare or define a hash function of your own. It is defined in the judge.
Input format: The first line contains N, the number of operations. Each of the next N lines is one operation. The operation name maps to methods as follows:
ADD server_id → add_server(server_id)ADD server_id v → add_server(server_id, v)REMOVE server_id → remove_server(server_id)GET chat_id → get_server(chat_id)AFFINITY server_id chat_id → record_affinity(server_id, chat_id)GET3 chat_id k → get_server_v3(chat_id, k)VRAM server_id chat_id → load_to_vram(server_id, chat_id)RAM server_id chat_id → load_to_ram(server_id, chat_id)GET4 chat_id k → get_server_v4(chat_id, k)Output format: For each GET, GET3, or GET4, print the resolved server_id on its own line. All other operations produce no output.
8
ADD alpha
ADD beta
ADD gamma
GET chat_1
GET chat_2
GET chat_3
GET chat_4
GET chat_5gamma
gamma
gamma
gamma
gamma4
ADD s1
GET chat_a
GET chat_b
GET chat_cs1
s1
s1