ctf

quantum

Description

Have you heard of quantum bitset?

nc quantum.challs.csc.tf 1337

Note: There is a time limit of 30 seconds on the remote.

quantum-dist.py server.py

Solution

First you need to solve the challenge with 2n ask queries

For ask

def ask(x0, y0):
    sum = 0
    for x, y in p:
        sum += abs(x - x0) + abs(y - y0)
    return sum

Let S = ask we have:

S(x0, y0) = Σ_(x, y) ∈ P {|x - x0| + |y - y0|}
S(x0, y0) = Σ_(x, y) ∈ P {(|x - x0| + |y|) + (|x| + |y - y0|) - (|x| + |y|)}
= Σ_(x, y) ∈ P {|x - x0| + |y|} + Σ_(x, y) ∈ P {|x| + |y - y0|} - Σ_(x, y) ∈ P {|x| + |y|}
= S(x, 0) + S(0, y) - S(0, 0)

Then ask all for x ∈ [0, n) S(x, 0) and for all y ∈ [0, n) S(0, y) You now have the answer of S(x, y) for all x, y after 2n queries

def ask(x0, y0):
    return S(x0, 0) + S(0, y0] - S(0, 0)

For query:

Σ_x ∈ [x0, x1] Σ_y ∈ [y0, y1] S(x, y)
= Σ_x ∈ [x0, x1] {(y1 - y0 + 1) * S(x, 0)}
+ Σ_y ∈ [y0, y1] {(x1 - x0 + 1) * S(0, y)}
- (x1 - x0 + 1) * (y1 - y0 + 1) * S(0, 0)

Can easily be done in O(1) using prefix sum

def query(x0, y0, x1, y1):
    return (
        (pSx[x1] - pSx[x0 - 1]) * (y1 - y0 + 1)
        + (x1 - x0 + 1) * (pSy[y1] - pSy[y0 - 1])
        - (x1 - x0 + 1) * (y1 - y0 + 1) * S(0, 0)
    )

You can refer to quantum.py to see how we implement these functions remotely

For 1.9n asks

This is only possible thanks to the help of Aeren. Thank you so much!!

Query all even positions -> for each odd positions, if the 3 even numbers around it are colinear, try to determines the value at that position, otherwise you query it

Refer to solve.py for the implementation

There are also an interesting solutions by participants:

By white

Basically the only optimization I did is first send every other point then if the change between 3 points I sent are same then we know the points in the middle has the same difference or well half the difference Then we run it a few times until we get a good rng on remote

Refer to solve_white.py for their implementation

By mfornet

Let f(x) := ask(x, 0) [It will be symmetric for y]

LEFT = (f(x + 1) - f(x) + n) / 2
RIGHT = n - LEFT

Due to 1 we only need to recover for each x, how many points has this coordinate.

f(i+3) = 3 * (LEFT - RIGHT) + f(i)
f(i+d) = d * (LEFT - RIGHT) + f(i)

With this observations we can compute f(i), f(i+1) and using 5 and 6 we can try to find how many frequencies are zero without asking for all of them. We start asking for f(i+3), then f(i+5), and so on f(i+2k+1), until we find something that is non zero.

I’m attaching the python code I used to check (empirically) the number of queries required.

from random import randint


def main():
    # The bigger `n` is, less questions we need to ask
    # 450 is the smallest value that n can take.
    n = 450

    xs = set([randint(1, n) for _ in range(n)])
    queries = set()

    i = 0
    while i < n:
        if i + 3 >= n:
            # The last 3 elements are asked separately
            for j in range(i, n):
                queries.add(j)
            break

        queries.add(i)
        queries.add(i + 1)

        j = i + 3
        while j < n:
            queries.add(j)

            if j - 1 in xs or j - 2 in xs:
                # There are points at j - 1 or j - 2, so we need to ask for them.
                i = j - 1
                break

            # Yay, we don't need to ask for `j - 1`.

            j += 2
            i = j

    # Multiplying by 2 is ok, because we repeat this in both dimensions, and they are independent.
    print(2 * len(queries) / n)