【2021杭电多校第一场】1005 Xor Sum

题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=6955

题目大意:给一个长度为n的序列,求一个区间[l,r]使a[l]^a[l+1]^...^a[r]>=k,输出满足条件最短的区间长度。

解题思路:(我是笨蛋看题解依然不会,感谢王同学的耐心讲解)

对于异或有以下性质:

如果a ^ b =c;那么 c ^ a = b 我们不难发现这个可以用前缀和来求
因此对于l ^ (l + 1)……^r =sun[r] ^ sum[l - 1] 
如果我们要查询区间[l,r]的异或和>=k 
其实只需要查询sum[r]^sum[l-1]>=k的l-r+1最小 
枚举r,查找离r最近的l是什么 不断更新答案(保存更大的l) 
但是这样时间复杂度是O(n^2) 数据范围是1e5肯定会TLE
因此可以建立01字典树,将查询时间优化到logn

代码实现:(细节在注释里)

#include <bits/stdc++.h>
using namespace std;
const int Mn = 1e5 + 6;
int tree[Mn * 31][2];
int mx[Mn * 31];
int k,cnt = 0;
void Add(int k,int r){//添加一个1-r的前缀异或为k的数值
    int a = 0;
    for(int i = 29;i >= 0;i --){
        int base = k >> i;
        base &= 1;//第i位(从低位数)
        if(tree[a][base] == 0) tree[a][base] = ++cnt;//新建节点
        a = tree[a][base];
        mx[a] = max(mx[a],r);//相同值,选r更大,更靠右的,这个是更新l 
    }
}
//now是当前路径值
//bit是当前查询到第几位
//w是前缀的值
//a是节点编号
int Query(int now,int bit,int w,int a){
    if(now >= k) return mx[a];//当前值大于k,满足条件直接返回 
    if(now + (1 << (bit + 1)) - 1 < k) return 0;
    int a1,a2;
    a1 = a2 = 0;//字典树返回的值 
	//提取出当前w的bit位和1异或
    if(tree[a][1]) a1 = Query(now + ((w & (1 << bit)) ^ (1 << bit)),bit - 1,w,tree[a][1]);
    if(tree[a][0]) a2 = Query(now + ((w & (1 << bit)) ^ (0 << bit)),bit - 1,w,tree[a][0]);
    return max(a1,a2);
}
void Build(int a){
    //初始化整棵树为0
    if(tree[a][1]) Build(tree[a][1]);
    if(tree[a][0]) Build(tree[a][0]);
    tree[a][1] = tree[a][0] = 0;
}
void inint(){
    Build(0);
    for(int i = 0;i <= cnt;i ++) mx[i] = 0;
    cnt = 0;
}
void Slove(){
    int n;
    inint();
    scanf("%d%d",&n,&k);
    int a,x;a = 0;
    int l,r;
    l = 0;
    r = n + 1;//ansl,ansr
    for(int i = 1;i <= n;i ++){
        scanf("%d",&x);
        a ^= x;
        int res = Query(0,29,a,0);//返回的是当前节点异或某个前缀大于k的右侧值
        if(res){
            //如果sum[5] ^ sum[3] >= k 的话区间应该为4 ~ 5 即(3 ++) ~ 5(sum[r] ^ sum[l - 1])
            res ++;//所以res++
            if(i - res < r - l)//如果当前答案更优,那就更新区间l,r 
                l = res,r = i;
        }
        Add(a,i);  
    }
    if(l) printf("%d %d\n",l,r);
    else puts("-1");
}
int main(){
    int _;scanf("%d",&_);
    while(_--) Slove();
    return 0;
}

上一篇:「Leetcode-算法_Easy461」通过「简单」题目学习位运算


下一篇:AT4142 [ARC098B] Xor Sum 2(尺取法)