01字典树(区间异或值)

01字典树在异或问题的查询上十分高效,它是按位插入和查询的,因为一个数若它的高位较大,那么这个数的值较大。所以插入和查询都是从最高位开始进行的。
1. 01字典树是一棵最多32层的二叉树,其每个节点的两条边分别代表二进制的某一位的值为0还是1.将某个路径上的值连起来就得到一个二进制串
2. ch[i]表示一个节点,ch[i][0]和ch[i][1]表示节点的两条边指向的节点,val[i]表示节点的值
3. 每个节点主要是4个属性:节点值、节点编号、两条边指向的下一节点的标号
4. 节点val值为0时表示到当前节点为止不能形成一个数,否则val[i]=数值
5. 可以通过贪心的策略来寻找与X异或结果最大的数,即优先找和X二进制的未处理的最高位值不相同的边对应的点,这样结果最大

#include 
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int tol; //节点标号
ll val[maxn * 32]; //点的值
int ch[maxn * 32][2]; //边的值
void init() {
    tol = 1;
    ch[0][0] = ch[0][1] = 0;
}
//向01字典树中插入x
void insert(ll x) {
    int u = 0;
    for(int i = 32; i >= 0; --i) {
        int v = (x >> i) & 1;
        if(!ch[u][v]) { //初始化节点
            ch[tol][0] = ch[tol][1] = 0; //当前节点的边初始化
            val[tol] = 0; //节点值为0
            ch[u][v] = tol++; //边指向的节点编号
        }
        u = ch[u][v];
    }
    val[u] = x;
}
ll query(ll x) {
    int u = 0;
    for(int i = 32; i >= 0; --i) {
        int v = (x >> i) & 1;
        if(ch[u][v ^ 1]) u = ch[u][v ^ 1];
        else u = ch[u][v];
    }
    return val[u];
}
int main()
{
    int t;
    scanf("%d", &t);
    for(int cas = 1; cas <= t; ++cas) {
        printf("Case #%d:\n", cas);
        int n, m;
        scanf("%d%d", &n, &m);
        init();
        for(int i = 0; i < n; ++i) {
            int x;
            scanf("%d", &x);
            insert(x);
        }
        for(int i = 0; i < m; ++i) {
            int x;
            scanf("%d", &x);
            printf("%d\n", query(x));
        }
    }
}

你可能感兴趣的:(总结)