inlineintget(char c){ if (c <= '9') return c - '0'; elseif (c <= 'Z') return c - 'A' + 10; elsereturn c - 'a' + 36; } /* 这个函数用来压缩字符集,将 '0'~'9' 映射到 0~9 'A'~‘Z’ 映射到 10~35 ‘a’~'z' 映射到 36~61 当然你也可以不写函数直接用 ASCII 码表,但是这样 M 的大小是 127 而且会有很多浪费 */
voidinsert(string &s){ // 引用传参 int p = 0; // 指针,表示我现在遍历到哪个点上了 for (auto i : s) { // 遍历字符串,这种语法需要 C++11,比赛可用 int z = get(i); // 获取到映射的值 if (!trie[p][z]) trie[p][z] = ++idx; // 第一次加入这条边,于是给它所到达的那个点分配一个新的编号 p = trie[p][z]; // 跳下去 ++cnt[p]; // 记录这个点多了一个字符串,用于计数或者删除字符串的时候 } end[p] = 1; // 记录一下这个点是某个字符串的结尾而不是只是一个路过的 }
intquery(string &s){ // 和 insert 是类似的,这里是查询以 s 作为前缀的字符数量 int p = 0; for (auto i : s) { int z = get(i); if (!trie[p][z]) return0; p = trie[p][z]; } // if (!end[p]) return 0; // 加上这一行就是必须要完全相同 return cnt[p]; }
voidupd(int x){ int p = 0; for(int i = L - 1;i >= 0;--i) { int z = x >> i & 1; if (!trie[p][z]) trie[p][z] = ++idx; p = trie[p][z]; ++cnt[p]; } }
voidrme(int x){ // 删数,本题要用 int p = 0; for(int i = L - 1;i >= 0;--i) { int z = x >> i & 1; p = trie[p][z]; --cnt[p]; } }
intgetMax(int x){ // 求字典树任选数字异或 x 得到的最大值 int p = 0, ret = 0; for(int i = L - 1;i >= 0;--i) { int z = x >> i & 1; if (cnt[trie[p][z ^ 1]]) ret |= 1 << i, p = trie[p][z ^ 1]; /* 可以不同,那就不同; 在左起第 i 位有一个 1,贡献是 1 << i, 由于只有第 i 次遍历会对该位产生影响因此这里是二进制或,和直接加法是一样的 */ else p = trie[p][z]; // 否则只能走相同的路 } return ret; }
intgetMin(int x){ // 同上,最小值 int p = 0, ret = 0; for(int i = L - 1;i >= 0;--i) { int z = x >> i & 1; if (cnt[trie[p][z]]) p = trie[p][z]; else p = trie[p][z ^ 1], ret |= 1 << i; } return ret; }
constint N = 2e6 + 10; int n, a[N], mn = INF, mx = -INF, idx, cnt[N], trie[N][2];
template <typename T>
inline T read(){ T sum = 0, fl = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fl = -1; for (; isdigit(ch); ch = getchar()) sum = sum * 10 + ch - '0'; return sum * fl; }
template <typename T>
inlinevoidwrite(T x){ if (x < 0) { putchar('-'), write<T>(-x); return; } staticchar sta[40]; short top = 0; do { sta[top++] = (x % 10) + 48, x /= 10; } while (x); while (top) putchar(sta[--top]); }
voidupd(int x){ int p = 0; for(int i = L - 1;i >= 0;--i) { int z = x >> i & 1; if (!trie[p][z]) trie[p][z] = ++idx; p = trie[p][z]; ++cnt[p]; } }
voidrme(int x){ int p = 0; for(int i = L - 1;i >= 0;--i) { int z = x >> i & 1; // 一定是存在的,因此不用判断是否存在 p = trie[p][z]; --cnt[p]; } }
intgetMax(int x){ int p = 0, ret = 0; for(int i = L - 1;i >= 0;--i) { int z = x >> i & 1; if (cnt[trie[p][z ^ 1]]) ret |= 1 << i, p = trie[p][z ^ 1]; else p = trie[p][z]; } return ret; }
intgetMin(int x){ int p = 0, ret = 0; for(int i = L - 1;i >= 0;--i) { int z = x >> i & 1; if (cnt[trie[p][z]]) p = trie[p][z]; else p = trie[p][z ^ 1], ret |= 1 << i; } return ret; }