【数据结构】01Trie

发布于:2025-05-09 ⋅ 阅读:(15) ⋅ 点赞:(0)

什么是 01Trie?

01Trie是字典树的一种变种,其只有两种情况,即 0 和 1,实现方式其实和字典树是一样的


有什么用呢?

其一般用于解决异或问题,是一种快速的数据结构,某些情况下可以无脑套用


实现方式?

和上面说的一样,其实就是字典树,我们利用实战来讲解一下


实战运用!

P10471 最大异或对 The XOR Largest Pair - 洛谷

思路:

板子

由于要让我们选择两个数使得异或和最大,如果我们直接双重循环的话指定会爆,那我们就需要优化了,而这时就可以请出我们的01Trie了,倒不如说这就是为它而生的

对于01Trie我们必备的两个操作就是插入和查询,我们先讲解插入

我们定义 ch[][] 数组为整个01Trie的大小,由于每个数都有31位,且每位都有01两种可能,所以数组大小就是 ch[N*31][2],同时我们再定义一个 idx,其代表当前已经有了多少个节点,其中 ch[p][j]代表的是节点 p 到 j 是否存在一条边,且这条边的另一个点是谁,这样我们就能每次跳到下一个节点了,如果不存在我们就只需要把它变成新的节点即可

那么对于每次插入操作,我们都从高到低插入(之后会说为什么),然后判断是否存在这条边,如果存在那就跳到下一个节点,否则就赋予新的节点再跳

查询操作也很好写了,我们和上面插入操作类似,只不过这次我们要主动跳跃了,我们判断 x 的当前位是什么,如果是 0,那我们就判断此点与 1 是否有边,如果有那就跳,否则就跳 0,如果是 1 也是同理,那为什么要从最高位开始呢?因为如果对于 0 最高位有 1 的话,那我们跳到 1 即可,因为此后即使全是 1 也不可能比此时选 1 大,如 1000 0111

那么按照上面思路写完就结束了

代码:

#include <iostream>
#include <algorithm>
#include<cstring>
#include <iomanip>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <utility>
#include <array>
#include <tuple>
using namespace std;
#define int long long
#define yes cout << "YES" << endl
#define no cout << "NO" << endl

const int N = 100010;
int n, a[N];
int ch[N * 31][2], idx = 0;

void insert(int x)
{
    int p = 0;
    for (int i = 30; i >= 0; i--)
    {
        int j = x >> i & 1;
        if (!ch[p][j])
        {
            ch[p][j] = ++idx;
        }
        p = ch[p][j];
    }
}

int query(int x)
{
    int p = 0, res = 0;
    for (int i = 30; i >= 0; i--)
    {
        int j = x >> i & 1;
        if (ch[p][!j])
        {
            res += 1 << i;
            p = ch[p][!j];
        }
        else
        {
            p = ch[p][j];
        }
    }
    return res;
}

void solve()
{
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        cin >> a[i];
        insert(a[i]);
    }
    int ans = 0;
    for (int i = 0; i < n; i++)
    {
        ans = max(ans, query(a[i]));
    }
    cout << ans;
}
signed main()
{
    //cin.tie(0)->sync_with_stdio(false);
    int t = 1;
    //cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

P4551 最长异或路径 - 洛谷

思路:

思维题

这题乍一看根本不知道怎么和01Trie扯上关系,但是我们要仔细分析一下

对于这棵树,我们随便以一点为根,这里以 1 为根,那么我们可以计算出每个点 x 到 1 的异或和val(1,x)是多少,一次 dfs 即可,那么知道这个有什么用呢?

对于 u v 两个点,如果我们要计算 u ~ v 的异或和,由于异或有交换律,所以我们不需要考虑顺序,那么 val(u,v) = val(1,u) ^ val(1,v),为什么呢?显然我们可以以 v 为一个中转站,然后链接 u v,如果其中有重复的路,由于 x ^ x = 0,所以重复的位置不会对答案造成影响,因此是可以这样的

所以题目就可以这样做:先 dfs 跑一边把 a[i] 求出来,然后将所有的 a[i] 加到 01Trie 中,然后跑一遍模板即可

代码:

#include <iostream>
#include <algorithm>
#include<cstring>
#include <iomanip>
#include<cctype>
#include<string>
#include <set>
#include <vector>
#include <cmath>
#include <queue>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <stack>
#include <utility>
#include <array>
#include <tuple>
using namespace std;
#define int long long
#define yes cout << "YES" << endl
#define no cout << "NO" << endl
const int N = 100010;
int n;
int ch[N * 31][2],idx = 0;
vector<vector<pair<int, int>>> g(N);
vector<int> a(N);

void insert(int x)
{
    int p = 0;
    for (int i = 30; i >= 0; i--)
    {
        int j = x >> i & 1;
        if (!ch[p][j])
        {
            ch[p][j] = ++idx;
        }
        p = ch[p][j];
    }
}

int query(int x)
{
    int p = 0,res = 0;
    for (int i = 30; i >= 0; i--)
    {
        int j = x >> i & 1;
        if (ch[p][!j])
        {
            res += 1LL << i;
            p = ch[p][!j];
        }
        else
        {
            p = ch[p][j];
        }
    }
    return res;
}

void dfs(int self,int fa)
{
    for (auto son : g[self])
    {
        if (son.first == fa)
        {
            continue;
        }
        a[son.first] = a[self] ^ son.second;
        dfs(son.first, self);
    }
}

void solve()
{
    cin >> n;
    for (int i = 0; i < n - 1; i++)
    {
        int u, v, x;
        cin >> u >> v >> x;
        g[u].push_back({ v,x });
        g[v].push_back({ u,x });
    }
    dfs(1, 1);
    for (int i = 1; i <= n; i++)
    {
        insert(a[i]);
    }
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        ans = max(ans, query(a[i]));
    }
    cout << ans;
}
signed main()
{
    //cin.tie(0)->sync_with_stdio(false);
    int t = 1;
    //cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到