二叉字典树(01Trie树)解决最大异或和区间问题

t组数据

每组n个值

求出最大异或和区间的总异或值

利用了异或的性质, 应该也可以用线段树维护区间的想法做(类似线段树维护最大区间和, 利用前缀和后缀的想法)

假设输入了第四个数x4

则二叉字典树中已经存储了 x1、x1^x2、x1^x2^x3 的值, 此时计算出 x1^x2^x3^x4 的值并进行询问, 尽量取每一个二进制位相反使得结果该二进制位为1,  由于相同数异或为0后可消去的性质, 可以直接得出1~4区间内的最大区间异或和, 然后与前最大值ans比较, 最终得出整个序列的最大异或区间的总异或值

#define _CRT_SECURE_NO_WARNINGS 1
#include<bits/stdc++.h>
#include <unordered_set>
typedef long long ll;

using namespace std;
int t;
int arr[100010];
int n;

int son[100010 * 31][2];          //二叉字典树
int cnt[100010 * 31];                  //节点i出现次数
int idx = 0;

void insert(int x, int v)        //x表示要插入或删除的数字, v为1或-1, 表示插入或删除
{
    int p = 0;
    for (int i = 30; i >= 0; i--)
    {
        int u = (x >> i) & 1;   //取出x的二进制前31位
        if (!son[p][u])         //若p节点没有u位上的孩子, 若已有则无需执行
        {
            //idx++;              //制造一个新下标用于指向p节点的新孩子
            son[p][u] = ++idx;    
        }
        p = son[p][u];          //前往下一个节点
        cnt[p] += v;            //下一个节点计数增加或减少
    }
}

int query(int x)
{
    int p = 0, r = 0;
    for (int i = 30; i >= 0; i--)
    {
        int u = (x >> i) & 1;
        if (cnt[son[p][!u]])         //如果有反向点, 就顺着反向点走
        {
            p = son[p][!u];
            r = r * 2 + 1;
        }
        else                    //没有反向点就顺着原方向走
        {
            p = son[p][u];
            r = r * 2;
        }
    }
    return r;
}


int main() 
{
    scanf("%d", &t);
    while (t--) 
    {
        for (int i = 0; i <= idx+1; i++) {
            son[i][0] = son[i][1] = 0;
            cnt[i] = 0;
        }
        idx = 0;

        scanf("%d", &n);


        int ans = 0, s = 0;
        arr[0] = 0;
        insert(arr[0], 1);

        for (int i = 1; i <= n; i++)
        {
            int x;
            scanf("%d", &x);
  
            arr[i] = arr[i - 1] ^ x;
            
            ans = max(ans, query(arr[i]));
            insert(arr[i], 1);
        }

        printf("%d\n", ans);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值