区间异或最大

版权声明: https://blog.csdn.net/weixin_39778570/article/details/82015968

首先我们看这样一个问题:给定一个包含N个整数的集合S={A1, A2, A3, … AN}。然后有M个询问,每次询问给定一个整数X,让你找一个Ai使得Ai xor X的值最大。
这道题也是可以用Trie解决的。首先我们知道一个整数可以用二进制表示成一个01串。比如3=(011)2, 5=(101)2, 4=(100)2……。我们假设输入的整数都在0~2^32-1之间,于是我们可以用一个长度是32位的01串表示一个整数。
然后对于给定的N个整数A1, A2, A3, … AN,我们把它们对应的01串都插入到一个trie中。注意这里字符集只有0和1,所以整个trie是一棵二叉树。(要转换为32位)
下面我们举一个例子,为了描述方便,我们假设整数都在0~7之间,也就是可以用3位01串表示。现在假设S={1, 2, 7},也就是说我们要在Trie中插入{001, 010, 111}:
这里写图片描述
这时假设我们要查询x=4,也就是哪个数和4异或结果最大?4=(100)2,我们的做法是在trie树中,尽量与4的二进制位反着走。比如4的第一位(最高位)是1,我们从0出发第一步就尽量沿着0走。因为我们要异或和最大,01相反才能异或值是1。并且这一步是可以贪心的,也就是说如果有相反的边,那么我们一定沿着这条边走。因为最高位异或得1的话,即便后面都是0, 10000…000也要比最高位是0,后面都是1的011111…111大。
所以我们第一步沿着标识是0的边,移动到了1号节点;4第二位是0,所以我们沿着标识是1的边移动到4号节点;4的第三位是0,但是4号节点没有标识是1的边,所以我们也只好沿着标识是0的边移动到5号节点。已经到了终结点,所以5号节点对应的A2=(010)2=2就是我们要求的答案,A2 xor 4 = 6是最大的。
我们先再看一道进阶版的题目:给定一个包含N个整数的数组A=[A1, A2, A3, … AN],让你求出一段连续的子数组A[l], A[l+1], … A[r]使得异或和A[l] xor A[l+1] xor A[l+2] xor … xor A[r]的值最大。
解决这道题目的关键,就是利用之前我们在枚举中提到的一个技巧,就是用前缀和表示部分和。之前我们遇到的部分和题目是加法的和,这里异或的和其实是一样的。我们定义S[i]=A[1] xor A[2] xor A[3] xor … xor A[i],那么A[l] xor A[l+1] xor A[l+2] xor … xor A[r]就等于S[r] xor S[l-1]。因为两个相同的数异或起来值是0嘛。而0异或任何数都是原来的数。

#define _CRT_SBCURE_NO_DEPRECATE
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>

using namespace std;

#define maxn 3300005
#define maxm 2
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1

int trie[maxn][maxm] = {0};
int color[maxn] = {0}, a[100000];

int t,n,m,ans,k=1,b[32];
int nowp;
void insert(int x){
    for(int i=32; i>=0; i--){
        b[i] = x&1;
        x >>= 1;
    }
    int p = 0;
    for(int i=0; i<32; i++){
        int c = b[i];
        if(!trie[p][c]){
            trie[p][c] = k;
            k++;
        }
        p = trie[p][c];
    }
    color[p] = 1;
}

int search(int x){
    int ret = 0;
    for(int i=31; i>=0; i--){
        b[i] = x&1;
        x >>= 1;
    }
    int p = 0;
    for(int i=0; i<32; i++){
        // 贪心匹配
        int c = 1-b[i];  // 取反 
        if(!trie[p][c]){
            p = trie[p][1-c];  // 自己一定存在树中 
            ret = ret<<1;
        }else{
            p = trie[p][c];
            ret = ret << 1 | 1;
        }
    }
    nowp = p;
    return ret;
}

int main(){
    scanf("%d", &t);
    int i = 1;
    while(t--){
        memset(trie, 0, sizeof(trie));
        memset(color, 0, sizeof(color));
        insert(0);  // 初始化0
        ans = 0;
        int stp = 0;
        int enp = 0;
        int s = 0;
        scanf("%d", &n);
        for(int i=0; i<n; i++){
            scanf("%d", &a[i]);
            s = s ^ a[i];
            //insert(s);
            int tmp = search(s);
            if(tmp > ans) {
                ans = tmp;  
                stp = (nowp-1)/32+1;
                enp = i;
            }
            insert(s);
        } 
        printf("Case #%d:", i++);
        printf("%d %d\n", stp+1,enp+1);  // 范围 
        //printf("%d\n", ans);   // 大小 
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_39778570/article/details/82015968
今日推荐