01-trie练习

一般习惯用递归实现01-trie, 可以看做是区间长度为2的幂的权值线段树, 能实现权值线段树的所有操作, 同时还可以实现一些异或操作

const int N = 1<<20;
struct {int ch[2],sum;} tr[N<<1];                                                 

void ins(int &o, int d, int x) {
    if (!o) o=++tot;
    ++tr[o].sum;
    if (d>=0) ins(tr[o].ch[x>>d&1],d-1,x);
}

这里用结构体存储$trie$树, $d$表示当前深度, $x$为插入元素, 叶结点深度为$-1$

假设元素范围均在$[0,1<<20)$, 若插入一个数$x$可以写成 $ins(T,19,x)$

空间占用是$O(N)$的, 由于是动态开点, $tr$数组只需开到$2N$即可

若元素范围过大, 空间占用是$O(mlogN)$, 一般要开到$40m$, $m$是操作次数

练习1 : CF 842D Vitya and Strange Lesson

大意: 给定$n$个数的集合, 每次将集合中所有数异或$x$, 求整个集合的mex

#include <iostream>
#include <algorithm>
#include <math.h>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
using namespace std;

const int N = 1<<19;
int n, m, tot, T;
int vis[N];
struct {int ch[2],sum;} tr[N<<2];

void ins(int &o, int d, int x) {
    if (!o) o=++tot;
    ++tr[o].sum;
    if (d>=0) ins(tr[o].ch[x>>d&1],d-1,x);
}

int query(int o, int d, int x) {
    if (d<0) return 0;
    int t = x>>d&1;
    if (tr[tr[o].ch[t]].sum==(1<<d)) return (1<<d)^query(tr[o].ch[t^1],d-1,x);
    return query(tr[o].ch[t],d-1,x);
}


int main() {
    scanf("%d%d", &n, &m);
    REP(i,1,n) { 
        int t;
        scanf("%d", &t);
        if (vis[t]) continue;
        vis[t] = 1;
        ins(T,18,t);
    }
    int num = 0;
    REP(i,1,m) {
        int t;
        scanf("%d", &t);
        printf("%d\n", query(T,18,num^=t));
    }
}

练习2

猜你喜欢

转载自www.cnblogs.com/uid001/p/10256496.html
今日推荐