---
title: Choosing the Commander
tags:
  - Trie Tree
id: "864"
categories:
  - - OI
    - String
date: 2017-06-30 15:20:25
---

## 题目描述

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 $p_i$. Each commander has two characteristics — his personality $p_j$ and leadership $l_j$ (both are integer numbers). Warrior $i$ respects commander $j$ only if $p_i \oplus p_j \lt l_j$ ($x \oplus y$ 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 $p_i$ - one warrior with personality $p_i$ joins Vova's army;
- 2 $p_i$ - one warrior with personality $p_i$ leaves Vova's army;
- 3 $p_i$ $l_i$ - Vova tries to hire a commander with personality $p_i$ and leadership $l_i$.

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.

## 题意概述

有一个初始为空的数字集合。有三种操作：① 向集合中加入一个数$p$；② 从集合中删除一个数$p$；③ 询问集合中与$p$的异或值小于$l$的数的个数。总共进行了$q$次操作。

数据范围：$1 \le q \le 10^5, \; 1 \le p_i, l_i \le 10^8$。

## 算法分析

根据集合中数的二进制建立 Trie 树，记录下每个节点子树的大小。对于每次询问，从高到低枚举$p, l$二进制的第$k$位，用$p_k, l_k$表示。设当前走到的节点为$t$，下一位为$s$则走到节点$t_s$。若$l_k=1$，则节点$t_{p_k}$所表示的数一定小于$l$；若$l_k=0$，则节点$t_{1-p_k}$所表示的数一定大于$l$。

## 代码

```cpp
#include <iostream>
using namespace std;
int q, o, p, l;
struct trie_tree {
    int tot = 1;
    struct node_type {
        int sum, child[2];
    } a[2000001];
    void insert(int t) {
        int r = 1; ++a[r].sum;
        for (int i = 26; i >= 0; --i) {
            if (!a[r].child[(t >> i) & 1]) a[r].child[(t >> i) & 1] = ++tot;
            r = a[r].child[(t >> i) & 1], ++a[r].sum;
        }
    }
    void remove(int t) {
        int r = 1; --a[r].sum;
        for (int i = 26; i >= 0; --i) {
            r = a[r].child[(t >> i) & 1], --a[r].sum;
        }
    }
    int find(int p, int l) {
        int r = 1, ret = 0;
        for (int i = 26; i >= 0; --i) {
            int s = (p >> i) & 1, t = (l >> i) & 1;
            if (t && a[r].child[s]) ret += a[a[r].child[s]].sum;
            if (t && a[r].child[!s]) r = a[r].child[!s];
            else if (!t && a[r].child[s]) r = a[r].child[s];
            else break;
        }
        return ret;
    }
} tree;
int main()
{
    cin >> q;
    while (q--) {
        cin >> o;
        switch (o) {
            case 1: {
                cin >> p, tree.insert(p);
                break;
            }
            case 2: {
                cin >> p, tree.remove(p);
                break;
            }
            default: {
                cin >> p >> l;
                cout << tree.find(p, l) << endl;
            }
        }
    }
    return 0;
}
```
