1. 程式人生 > 實用技巧 >01 Trie 專題

01 Trie 專題

01 Trie 專題

異或最大值

The xor largest pair

題意: 異或最大值的模板。一個數和一個序列中一個數的異或最大值是多少?要支援詢問。

思路:考慮把序列插入,構建一個 \(\text{Trie}\) 樹。那麼在詢問時,只需要討論該數的位是 \(0\) 還是 \(1\) 就行了。這樣就需要 \(O(n\log w)\) 的預處理,\(O(\log w)\) 的詢問和修改,為什麼是對的。因為異或中我們如果可以滿足最高位,那麼沒有理由不改變最高位,因為 \(2^{bit} >\sum_{i=0}^{i < bit}2^i\) 。那麼由高位到地位貪心就可以了。

/*
https://ac.nowcoder.com/acm/problem/50993
*/
#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e6 + 100;
int ch[maxn][2];

int sz = 0, rt, n;

int read() {int x; scanf("%d", &x); return x;}

void insert(int u, int t, int x){
    if(t < 0) return;
    int f = ((x >> t) & 1);
    if(!ch[u][f]) ch[u][f] = ++sz;
    insert(ch[u][f], t-1, x);
}

int ask(int u, int t, int x){
    if(t < 0) return 0;
    int f = ((x >> t) & 1);
    if(ch[u][!f]) return ask(ch[u][!f], t-1, x) + (1 << t);
    
    return ask(ch[u][f], t-1, x);
}

int main(){
    n = read();
    int ans = 0;
    rt = ++ sz;
    for(int i = 0; i < n; i++){
        int x = read();
        insert(rt, 30, x);
        if(i != 0) ans = max(ans, ask(rt, 30, x));
    }
    printf("%d\n", ans);
    return 0;
}

Xor Sum

Problem Description

Zeus 和 Prometheus 做了一個遊戲,Prometheus 給 Zeus 一個集合,集合中包含了N個正整數,隨後 Prometheus 將向 Zeus 發起M次詢問,每次詢問中包含一個正整數 S ,之後 Zeus 需要在集合當中找出一個正整數 K ,使得 K 與 S 的異或結果最大。Prometheus 為了讓 Zeus 看到人類的偉大,隨即同意 Zeus 可以向人類求助。你能證明人類的智慧麼?

Input

輸入包含若干組測試資料,每組測試資料包含若干行。
輸入的第一行是一個整數\(T(T < 10)\),表示共有T組資料。
每組資料的第一行輸入兩個正整數\(N,M(<1=N,M<=100000)\)

,接下來一行,包含N個正整數,代表 Zeus 的獲得的集合,之後M行,每行一個正整數S,代表 Prometheus 詢問的正整數。所有正整數均不超過\(2^{32}\)

Output

對於每組資料,首先需要輸出單獨一行”Case #?:”,其中問號處應填入當前的資料組數,組數從1開始計算。
對於每個詢問,輸出一個正整數K,使得K與S異或值最大。

Sample Input

2
3 2
3 4 5
1 5
4 1
4 6 5 6
3

Sample Output

Case #1:
4
3
Case #2:
4

同上題,簡單板子

程式碼
/*
http://acm.hdu.edu.cn/showproblem.php?pid=4825
*/
#include<bits/stdc++.h>
using namespace std;

int read(){int x; scanf("%d", &x); return x;}

const int maxn = 5e6 + 10;
int ch[maxn][2], value[maxn];
int a[maxn];
int sz = 1, rt = 1;
int m, n, t, T;

void insert(int u, int t, int x){
    if(t < 0) { value[u] = x; return; }
    int i = (x >> t) & 1;
    if(!ch[u][i]) ch[u][i] = ++sz;
    insert(ch[u][i], t - 1, x);
}

int query(int u, int t, int x){
    if(t < 0) return value[u];
    int i = (x >> t) & 1;
    if(ch[u][!i]) return query(ch[u][!i], t-1, x);
    return query(ch[u][i], t - 1, x);
}

void solve(){
    n = read(); m = read();
    memset(ch, 0, sizeof ch);
    sz = rt = 1;
    memset(value, 0, sizeof value);
    for(int i = 1; i <= n; i++) a[i] = read(), insert(rt, 31, a[i]);

    printf("Case #%d:\n", t - 1);
    for (int i = 1; i <= m; i++){
        int s = read();
        printf("%d\n", query(rt, 31, s));
    }
}

int main(){
    t = 1; T = read();
    while(t++ <= T) solve();
    return 0;
}

異或最小值

Vitya and Strange Lesson

\((a_1,a_2,a_3..a_n) \oplus A\oplus B\) 根據結合率,等價於 \((a_1,a_2,a_3..a_n) \oplus (A\oplus B)\)

因為我們要求的是 \(mex\) ,那麼我們就考慮 \(0\)\(mex-1\) 的數是否全部出現過。那麼就變為查詢異或最小值了,我們在構建 \(\text{01 Trie}\) 時要順便記錄 \(\text{Trie}\) 中的元素個數,也就是\(sz\),當一個節點的元素個數填滿時,我們是不能考慮的。

/*
https://ac.nowcoder.com/acm/problem/112209
*/
#include<bits/stdc++.h>
using namespace std;

const int maxn = 6e6 + 10;
int read(){ int x; scanf("%d", &x); return x;}
int sz[maxn], ch[maxn][2];
int size = 1, rt = 1;
int n, m, last;

void insert(int u, int t, int x){
    if(t < 0){sz[u] = 1; return;}
    int i = (x >> t) & 1;
    if(!ch[u][i]) ch[u][i] = ++size;
    insert(ch[u][i], t - 1, x);
    sz[u] = sz[ch[u][i]] + sz[ch[u][!i]];
}
int query(int u, int t, int x){
    if(t < 0 || u == 0) return 0;
    int i = (x >> t) & 1;
    if((1 << t) != sz[ch[u][i]]) return query(ch[u][i], t - 1, x);
    else return query(ch[u][!i], t - 1, x) + (1 << t);
}

int main(){
    n = read(); m = read(); 
    for(int i = 1; i <= n; i++) insert(rt, 20, read());
    while(m--){
        last ^= read();
        printf("%d\n", query(rt, 20, last));
    }
    return 0;
}

儲存值的異或最大值

奶牛異或

\(a\oplus a = 0\) ,我們可以考慮做一次字首異或和。那麼區間操作就變為單點操作了。

/*
https://ac.nowcoder.com/acm/problem/22998
*/
#include<bits/stdc++.h>
using namespace std;

const int maxn = 2e7 + 100;
int ch[maxn][2], id[maxn], a[maxn];

int sz = 0, rt, n;
int ans1, ans2 = 1, ans3 = 1;

int read() {int x; scanf("%d", &x); return x;}

void insert(int u, int t, int x, int ID){
    if(t < 0){ id[u] = ID; return; }
    int f = ((x >> t) & 1);
    if(!ch[u][f]) ch[u][f] = ++sz;
    insert(ch[u][f], t-1, x, ID);
}

int ask(int u, int t, int x){
    if(t < 0) return id[u];
    int f = ((x >> t) & 1);
    if(ch[u][!f]) return ask(ch[u][!f], t-1, x);
    
    return ask(ch[u][f], t-1, x);
}

int main(){
    n = read(); a[0] = 0;
    for(int i = 1; i <= n; i++) (a[i] = a[i-1] ^ read());
    rt = ++ sz;
    for(int i = 1; i <= n; i++){
        insert(rt, 22, a[i-1], i);
        int x = ask(rt, 22, a[i]) - 1;
        if(ans1 < (a[x] ^ a[i])){
            ans1 = (a[x] ^ a[i]);
            ans2 = x + 1;
            ans3 = i;
        }
    }
    printf("%d %d %d\n", ans1, ans2, ans3);
    return 0;
}

帶刪除的異或最小值

Perfect Security

我們在\(\text{01 Trie}\) 上再維護一個\(sz_i\) 標記,表示是否這個節點下還有沒有可用元素。那麼刪除和插入都只會影響一條鏈。

/*
https://ac.nowcoder.com/acm/problem/112567 
*/
#include<bits/stdc++.h>
using namespace std;

int read(){int x; scanf("%d", &x); return x;}

const int maxn = 6e6 + 10;
int ch[maxn][2], sz[maxn];
int size = 0, rt = 0;
int n, m;
int a[maxn], b[maxn], c[maxn];

void insert(int u, int t, int x){
    if(t < 0) return;
    int i = (x >> t) & 1;
    if(!ch[u][i])  ch[u][i] = ++size;;
    sz[ch[u][i]]++;
    insert(ch[u][i], t - 1, x);
}

void erase(int u, int t, int x){
    if(t < 0) return;
    int i = (x >> t) & 1;
    sz[ch[u][i]]--;
    erase(ch[u][i], t - 1, x);
}

int query(int u, int t, int x){
    if(t < 0) return 0;
    int i = (x >> t) & 1;
    if(sz[ch[u][i]]) return query(ch[u][i], t-1, x) + (i << t);
    else return query(ch[u][!i], t-1, x) + ((!i) << t);
}
int main(){
    n = read(); 
    for(int i = 1; i <= n; i++) a[i] = read();
    for(int i = 1; i <= n; i++) b[i] = read(), insert(rt, 30, b[i]);

    for(int i = 1; i <= n; i++){
        int x = query(rt, 30, a[i]);
        printf("%d\n", a[i] ^ x);
        erase(rt, 30, x);
    }
    return 0;
}

帶刪除的異或最大值

D. Vasiliy's Multiset

Author has gone out of the stories about Vasiliy, so here is just a formal task description.

You are given q queries and a multiset A, initially containing only integer 0. There are three types of queries:

  1. "+ x" — add integer x to multiset A.
  2. "- x" — erase one occurrence of integer x from multiset A. It's guaranteed that at least one x is present in the multiset A before this query.
  3. "? x" — you are given integer x and need to compute the value , i.e. the maximum value of bitwise exclusive OR (also know as XOR) of integer x and some integer y from the multiset A.

Multiset is a set, where equal elements are allowed.

Input

The first line of the input contains a single integer$q (1 ≤ q≤ 200 000) $— the number of queries Vasiliy has to perform.

Each of the following q lines of the input contains one of three characters '+', '-' or '?' and an integer \(x_i (1 ≤ x_i ≤ 10^9)\). It's guaranteed that there is at least one query of the third type.

Note, that the integer 0 will always be present in the set A.

Output

For each query of the type '?' print one integer — the maximum value of bitwise exclusive OR (XOR) of integer x**i and some integer from the multiset A.

input

10
+ 8
+ 9
+ 11
+ 6
+ 1
? 3
- 8
? 3
? 8
? 11

output

11
10
14
13

Note

After first five operations multiset A contains integers \(0, 8, 9, 11, 6 and 1.\)

The answer for the sixth query is integer — maximum among integers , , , and .

題意

初始有個一個空集,n個操作,操作分三種

  • +x,將一個 x 加入集合
  • -x,刪除集合內的一個 x
  • ?x,詢問集合中與 x 異或的最大值
程式碼
#include<bits/stdc++.h>
using namespace std;

const int maxn = 6e6 + 10;
int ch[maxn][2], sz[maxn], val[maxn], cnt[maxn];
int size = 0, rt = 0;
int n, m;

void insert(int u, int t, int x){
    if(t < 0) { val[u] = x; return;}
    int i = (x >> t) & 1;
    if(!ch[u][i])  ch[u][i] = ++size;
    sz[ch[u][i]]++;
    insert(ch[u][i], t - 1, x);
}

void erase(int u, int t, int x){
    if(t < 0) return;
    int i = (x >> t) & 1;
    sz[ch[u][i]]--;
    erase(ch[u][i], t - 1, x);
}

int query(int u, int t, int x){
    if(t < 0) return val[u];
    int i = (x >> t) & 1;
    if(sz[ch[u][!i]]) return query(ch[u][!i], t-1, x);
    else return query(ch[u][i], t-1, x);
}

int main(){
    cin >> n;
    
    for(int i = 1; i <= n; i++){
        char c; cin >> c;
        int t; cin >> t;
        if(c == '+') insert(rt, 30, t);
        else if(c == '-') erase(rt, 30, t);
        else  printf("%d\n", max(t, query(rt, 30, t) ^ t));
    }
    return 0;
}
/*
12
? 1
+ 1
+ 4
? 5
? 6
*/