Trie 学习笔记

模板及讲解

字典树

字典树是前缀树,支持$insert(s)$和$query(s)$操作
两个函数按照树结构往下走处理即可

维护二进制 (Xor)

poj 3764

给定一棵$n$个点的带权无向树,求树上路径异或和的最大值。

利用 Xor 性质,我们发现路径异或和满足容斥 (即$[l,r]$可由$[1,r],[1, l-1]$得出)
那么我们求根到每个点的异或和$d_i$,然后尝试如何异或出一条路径来
我们发现任意两个$d_i,d_j$的异或和为某条路径的异或和,且不漏解
那么问题转化为求序列$d_i$两两异或最大值。
因为 Xor 常用 Trie 维护,所以我们可以运用 Trie 求这个最大值。将所有$d_i$以二进制形式插入 Trie (高位在上,定一个最大位数,不够在前面补0),边权为二进制位,然后对于每个二进制下的$d_i$在Trie树上走尽量相反的边,因为这样保证异或后大。树上没有最优边那就只能走另一条边。这样下来的路径就是$d_i$与其他$d_j$的最大异或值。$O(n \cdot len)$操作即可,$len$为二进制最大位数。

Codeforces 842D

常见题型

Trie树
1、前缀问题
2、翻转字符串,处理后缀 jzoj 5397bzoj 4567
3、01Trie:Xor问题 CF 842Dpoj 3764
4、回文相关:Bzoj 1524 (多个回文串拼接问题)
Trie图:AC自动机
Fail树bzoj 2938
前缀关系树 (Trie树去除虚节点) bzoj 4567

可持久化Trie树相关代码

差分版

https://www.luogu.org/problem/P4592

#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<queue>
#include<set>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
#define fir first
#define sec second
#define mp make_pair
using namespace std;

namespace flyinthesky {

    const int MAXN = 100000 + 3, LEN = 30;

    int n, Q, vi[MAXN], idx, dfn[MAXN], l[MAXN], r[MAXN], fa[MAXN], pre[MAXN][22], dep[MAXN];
    vector<int > G[MAXN];

    int rt_1[MAXN], ch_1[MAXN * 33][2], cnt_1, siz_1[MAXN * 33];
    int rt_2[MAXN], ch_2[MAXN * 33][2], cnt_2, siz_2[MAXN * 33];

    void dfs(int u, int p) {
        fa[u] = p, dfn[++idx] = u, l[u] = idx;
        pre[u][0] = p, dep[u] = dep[p] + 1;
        for (int i = 1; i <= 20; ++i) pre[u][i] = pre[pre[u][i - 1]][i - 1];
        for (int i = 0; i < (int)G[u].size(); ++i) {
            int v = G[u][i];
            if (v != p) dfs(v, u);
        }
        r[u] = idx;
    }
    int LCA(int a, int b) {
        if (dep[a] < dep[b]) swap(a, b);
        for (int i = 20; i >= 0; --i) if (dep[pre[a][i]] >= dep[b]) a = pre[a][i];
        if (a == b) return a;
        for (int i = 20; i >= 0; --i) if (pre[a][i] != pre[b][i]) a = pre[a][i], b = pre[b][i];
        return pre[a][0];
    }

    void ins_1(int pre, int now, int i, int a) {
        siz_1[now] = siz_1[pre];
        if (i == -1) {siz_1[now]++; return ;}
        int c = (a >> i) & 1;
        ch_1[now][c] = ++cnt_1, ch_1[now][c ^ 1] = ch_1[pre][c ^ 1];
        ins_1(ch_1[pre][c], ch_1[now][c], i - 1, a);
        siz_1[now] += siz_1[ch_1[now][0]] + siz_1[ch_1[now][1]];
    }
    void ins_2(int pre, int now, int i, int a) {
        siz_2[now] = siz_2[pre];
        if (i == -1) {siz_2[now]++; return ;}
        int c = (a >> i) & 1;
        ch_2[now][c] = ++cnt_2, ch_2[now][c ^ 1] = ch_2[pre][c ^ 1];
        ins_2(ch_2[pre][c], ch_2[now][c], i - 1, a);
        siz_2[now] += siz_2[ch_2[now][0]] + siz_2[ch_2[now][1]];
    }
    int query_1(int pre, int now, int b) {
        int bs = (1 << LEN), ret = 0;
        for (int i = LEN; i >= 0; --i) {
            int c = (b >> i) & 1, whw = siz_1[ch_1[now][c ^ 1]] - siz_1[ch_1[pre][c ^ 1]];
            if (whw) ret += bs, pre = ch_1[pre][c ^ 1], now = ch_1[now][c ^ 1];
            else pre = ch_1[pre][c], now = ch_1[now][c];
            bs /= 2;
        }
        return ret;
    }
    int query_2(int u, int v, int lca, int flca, int b) {
        int bs = (1 << LEN), ret = 0;
        for (int i = LEN; i >= 0; --i) {
            int c = (b >> i) & 1, whw = siz_2[ch_2[u][c ^ 1]] + siz_2[ch_2[v][c ^ 1]] - siz_2[ch_2[lca][c ^ 1]] - siz_2[ch_2[flca][c ^ 1]];
            if (whw) ret += bs, u = ch_2[u][c ^ 1], v = ch_2[v][c ^ 1], lca = ch_2[lca][c ^ 1], flca = ch_2[flca][c ^ 1];
            else u = ch_2[u][c], v = ch_2[v][c], lca = ch_2[lca][c], flca = ch_2[flca][c];
            bs /= 2;
        }
        return ret;
    }


    void clean() {
    }
    int solve() {

        clean();
        cin >> n >> Q;
        for (int i = 1; i <= n; ++i) scanf("%d", &vi[i]);
        for (int x, y, i = 1; i < n; ++i) scanf("%d%d", &x, &y), G[x].push_back(y), G[y].push_back(x);
        dfs(1, 0);
        for (int i = 1; i <= n; ++i) {
            ins_1(rt_1[i - 1], rt_1[i] = ++cnt_1, LEN, vi[dfn[i]]);
            ins_2(rt_2[fa[i]], rt_2[i] = ++cnt_2, LEN, vi[i]);
        }
        while (Q--) {
            int tp; scanf("%d", &tp);
            if (tp == 1) {
                int x, gg; scanf("%d%d", &x, &gg);
                printf("%d\n", query_1(rt_1[l[x] - 1], rt_1[r[x]], gg));
            } else {
                int x, y, gg; scanf("%d%d%d", &x, &y, &gg);
                int l = LCA(x, y);
                printf("%d\n", query_2(rt_2[x], rt_2[y], rt_2[l], rt_2[fa[l]], gg));
            }
        }

        return 0;
    } 
}
int main() {
    flyinthesky::solve();
    return 0;
}
------ 本文结束 ------