Choosing The Commander (Codeforces 817E)
Tags: Others, bitmasks

As you might remember from the previous round, Vova is currently playing a strategic game known as Rage of Empires.Vova managed to build a large army, but forgot about the main person in the army - the commander. So he tries to hire a commander, and he wants to choose the person who will be respected by warriors.Each warrior is represented by his personality — an integer number pi. Each commander has two characteristics — his personality pj and leadership lj (both are integer numbers). Warrior i respects commander j only if  ( is the bitwise excluding OR of x and y).Initially Vova's army is empty. There are three different types of events that can happen with the army:  1 pi — one warrior with personality pi joins Vova's army;  2 pi — one warrior with personality pi leaves Vova's army;  3 pi li — Vova tries to hire a commander with personality pi and leadership li. For each event of the third type Vova wants to know how many warriors (counting only those who joined the army and haven't left yet) respect the commander he tries to hire.

Input: The first line contains one integer q (1 ≤ q ≤ 100000) — the number of events.Then q lines follow. Each line describes the event:  1 pi (1 ≤ pi ≤ 108) — one warrior with personality pi joins Vova's army;  2 pi (1 ≤ pi ≤ 108) — one warrior with personality pi leaves Vova's army (it is guaranteed that there is at least one such warrior in Vova's army by this moment);  3 pi li (1 ≤ pi, li ≤ 108) — Vova tries to hire a commander with personality pi and leadership li. There is at least one event of this type. 

Output: For each event of the third type print one integer — the number of warriors who respect the commander Vova tries to hire in the event.

Note: In the example the army consists of two warriors with personalities 3 and 4 after first two events. Then Vova tries to hire a commander with personality 6 and leadership 3, and only one warrior respects him (, and 2 < 3, but , and 5 ≥ 3). Then warrior with personality 4 leaves, and when Vova tries to hire that commander again, there are no warriors who respect him.

Sample Inputs:
5
1 3
1 4
3 6 3
2 4
3 6 3

Sample Outputs:
1
0

Solution (Python):
import sys
input = sys.stdin.buffer.readline

def binary_trie(l):
    (G0, G1, cnt) = ([-1], [-1], [0])
    return (G0, G1, cnt, l)

def insert(x):
    j = 0
    for i in range(l, -1, -1):
        cnt[j] += 1
        if x & pow2[i]:
            if G1[j] == -1:
                G0.append(-1)
                G1.append(-1)
                cnt.append(0)
                G1[j] = len(cnt) - 1
            j = G1[j]
        else:
            if G0[j] == -1:
                G0.append(-1)
                G1.append(-1)
                cnt.append(0)
                G0[j] = len(cnt) - 1
            j = G0[j]
    cnt[j] += 1
    return

def erase(x):
    j = 0
    for i in range(l, -1, -1):
        cnt[j] -= 1
        if x & pow2[i]:
            j = G1[j]
        else:
            j = G0[j]
    cnt[j] -= 1
    return

def xor_and_count_min(x, k):
    j = 0
    ans = 0
    for i in range(l, -1, -1):
        if not x & pow2[i]:
            if k & pow2[i]:
                if G0[j] ^ -1:
                    ans += cnt[G0[j]]
                j = G1[j]
            else:
                j = G0[j]
        elif k & pow2[i]:
            if G1[j] ^ -1:
                ans += cnt[G1[j]]
            j = G0[j]
        else:
            j = G1[j]
    return ans
q = int(input())
pow2 = [1]
for _ in range(31):
    pow2.append(2 * pow2[-1])
(G0, G1, cnt, l) = binary_trie(31)
ans = []
for _ in range(q):
    t = list(map(int, input().split()))
    p = t[1]
    if t[0] == 1:
        insert(p)
    elif t[0] == 2:
        erase(p)
    else:
        li = t[2]
        ans0 = xor_and_count_min(p, li)
        ans.append(ans0)
sys.stdout.write('\n'.join(map(str, ans)))

Solution (Python):
class BinaryTrie:

    def __init__(self, max_log: int=60, allow_multiple_elements: bool=True, add_query_limit: int=10 ** 6):
        self.max_log = max_log
        self.x_end = 1 << max_log
        self.v_list = [0] * (max_log + 1)
        self.multiset = allow_multiple_elements
        self.add_query_count = 0
        self.add_query_limit = add_query_limit
        n = max_log * add_query_limit + 1
        self.edges = [-1] * (2 * n)
        self.size = [0] * n
        self.is_end = [0] * n
        self.max_v = 0
        self.lazy = 0

    def xor_all(self, x: int):
        self.lazy ^= x

    def __ixor__(self, x: int):
        self.xor_all(x)
        return self

    def add(self, x: int):
        x ^= self.lazy
        v = 0
        for i in reversed(range(self.max_log)):
            d = (x >> i) % 2
            if self.edges[2 * v + d] == -1:
                self.max_v += 1
                self.edges[2 * v + d] = self.max_v
            v = self.edges[2 * v + d]
            self.v_list[i] = v
        if self.multiset or self.is_end[v] == 0:
            self.is_end[v] += 1
            for v in self.v_list:
                self.size[v] += 1
        self.add_query_count += 1

    def discard(self, x: int):
        if not 0 <= x < self.x_end:
            return
        x ^= self.lazy
        v = 0
        for i in reversed(range(self.max_log)):
            d = (x >> i) % 2
            if self.edges[2 * v + d] == -1:
                return
            v = self.edges[2 * v + d]
            self.v_list[i] = v
        if self.is_end[v] > 0:
            self.is_end[v] -= 1
            for v in self.v_list:
                self.size[v] -= 1

    def erase(self, x: int, count: int=-1):
        if not 0 <= x < self.x_end:
            return
        x ^= self.lazy
        v = 0
        for i in reversed(range(self.max_log)):
            d = (x >> i) % 2
            if self.edges[2 * v + d] == -1:
                return
            v = self.edges[2 * v + d]
            self.v_list[i] = v
        if count == -1 or self.is_end[v] < count:
            count = self.is_end[v]
        if self.is_end[v] > 0:
            self.is_end[v] -= count
            for v in self.v_list:
                self.size[v] -= count

    def count(self, x: int) -> int:
        if not 0 <= x < self.x_end:
            return 0
        x ^= self.lazy
        v = 0
        for i in reversed(range(self.max_log)):
            d = (x >> i) % 2
            if self.edges[2 * v + d] == -1:
                return 0
            v = self.edges[2 * v + d]
        return self.is_end[v]

    def __contains__(self, x: int) -> bool:
        return bool(self.count(x))

    def __len__(self):
        return self.size[0]

    def __bool__(self):
        return bool(len(self))

    def bisect_left(self, x: int) -> int:
        if x < 0:
            return 0
        if self.x_end <= x:
            return len(self)
        v = 0
        ret = 0
        for i in reversed(range(self.max_log)):
            d = (x >> i) % 2
            l = (self.lazy >> i) % 2
            lc = self.edges[2 * v]
            rc = self.edges[2 * v + 1]
            if l == 1:
                (lc, rc) = (rc, lc)
            if d:
                if lc != -1:
                    ret += self.size[lc]
                if rc == -1:
                    return ret
                v = rc
            else:
                if lc == -1:
                    return ret
                v = lc
        return ret

    def bisect_right(self, x: int) -> int:
        return self.bisect_left(x + 1)

    def index(self, x: int) -> int:
        if x not in self:
            raise ValueError(f'{x} is not in BinaryTrie')
        return self.bisect_left(x)

    def find(self, x: int) -> int:
        if x not in self:
            return -1
        return self.bisect_left(x)

    def kth_min_element(self, k: int) -> int:
        if k < 0:
            k += self.size[0]
        v = 0
        ret = 0
        for i in reversed(range(self.max_log)):
            l = (self.lazy >> i) % 2
            lc = self.edges[2 * v]
            rc = self.edges[2 * v + 1]
            if l == 1:
                (lc, rc) = (rc, lc)
            if lc == -1:
                v = rc
                ret |= 1 << i
                continue
            if self.size[lc] <= k:
                k -= self.size[lc]
                v = rc
                ret |= 1 << i
            else:
                v = lc
        return ret

    def minumum(self) -> int:
        return self.kth_min_element(0)

    def maximum(self) -> int:
        return self.kth_min_element(-1)

    def __iter__(self):
        q = [(0, 0)]
        for i in reversed(range(self.max_log)):
            l = (self.lazy >> i) % 2
            nq = []
            for (v, x) in q:
                lc = self.edges[2 * v]
                rc = self.edges[2 * v + 1]
                if l == 1:
                    (lc, rc) = (rc, lc)
                if lc != -1:
                    nq.append((lc, 2 * x))
                if rc != -1:
                    nq.append((rc, 2 * x + 1))
                q = nq
        for (v, x) in q:
            for _ in range(self.is_end[v]):
                yield x

    def __str__(self):
        prefix = 'BinaryTrie('
        content = list(map(str, self))
        suffix = ')'
        if content:
            content[0] = prefix + content[0]
            content[-1] = content[-1] + suffix
        else:
            content[-1] = content[-1] + suffix
        return ', '.join(content)

    def __getitem__(self, k):
        return self.kth_min_element(k)
import sys
import io, os
input = sys.stdin.readline

def main():
    q = int(input())
    bt = BinaryTrie(30, True, q)
    for i in range(q):
        temp = list(map(int, input().split()))
        if temp[0] == 1:
            p = temp[1]
            bt.add(p)
        elif temp[0] == 2:
            p = temp[1]
            bt.discard(p)
        else:
            (p, l) = temp[1:]
            bt.xor_all(p)
            print(bt.bisect_left(l))
            bt.xor_all(p)
if __name__ == '__main__':
    main()

Solution (Python):
import sys
from collections import defaultdict

class Node:

    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
q = int(sys.stdin.readline())
root = Node(0)
for _ in range(q):
    l = list(map(int, sys.stdin.readline().split()))
    if l[0] == 1:
        bit = 28
        cur = root
        num = l[1]
        while bit >= 0:
            if 1 << bit & num == 1 << bit:
                if cur.right is None:
                    cur.right = Node(1)
                else:
                    cur.right.val += 1
                cur = cur.right
            else:
                if cur.left is None:
                    cur.left = Node(1)
                else:
                    cur.left.val += 1
                cur = cur.left
            bit -= 1
    if l[0] == 2:
        num = l[1]
        (bit, cur) = (28, root)
        while bit >= 0:
            if 1 << bit & num == 1 << bit:
                cur.right.val -= 1
                cur = cur.right
            else:
                cur.left.val -= 1
                cur = cur.left
            bit -= 1
    if l[0] == 3:
        (res, cur, bit) = (0, root, 28)
        while bit >= 0:
            num = 1 << bit
            if num & l[2] and num & l[1]:
                if cur.right is not None:
                    res += cur.right.val
                if cur.left is None:
                    break
                cur = cur.left
                bit -= 1
                continue
            if num & l[2] and (not num & l[1]):
                if cur.left is not None:
                    res += cur.left.val
                if cur.right is None:
                    break
                cur = cur.right
                bit -= 1
                continue
            if not num & l[2] and num & l[1]:
                if cur.right is None:
                    break
                cur = cur.right
                bit -= 1
                continue
            if not num & l[2] and (not num & l[1]):
                if cur.left is None:
                    break
                cur = cur.left
                bit -= 1
                continue
        print(res)