HDU 6955 Xor sum (字典树 + 前缀)

链接:Xor sum
题意:
给一个长度为 n 的数组 , 要求出一个最短的连续子段,使它的异或和大于等于 k。

思路:
首先我们要知道 [1 , l]的前缀异或和与 [l , r]的前缀异或和 的异或为 [l + 1 , r] 的异或和。所以我们对于每一个右端点,用字典树查询左边的每一个前缀异或和 , 同时字典树存当前01串出现的最右位置。

代码:

#include <iostream>
#include <cstdio>
#include <queue>
#include <math.h>
#include <map>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 4e6 + 7;
int T;
int tot = 1,tire[maxn][3],pre[maxn];
int n , k , a[maxn];
void insert(int pos , int x) {
    int p = 1,t;
    for(int i = 30; i >= 0; i --){
        t = (x >> i) & 1;
        if(tire[p][t] == 0) {
            tire[p][t]= ++ tot;
        }
        p=tire[p][t];
        pre[p] = max(pre[p] , pos);
    }
}
int find(int pos ,int now ,  int x){
    if(pos == -1) return pre[now];
    if(now == 0) return 0;
    int l = tire[now][0];
    int r = tire[now][1];
    if((k >> pos) & 1){
        if((x >> pos) & 1){
            return find(pos - 1 , l , x);
        }
        return find(pos - 1 , r , x);
    }
    else{
        if((x >> pos) & 1){
            return max(find(pos - 1 , r , x) , pre[l]);
        }
        return max(find(pos - 1 , l , x) , pre[r]);

    }
}
void init(){
    for(int i = 0; i <= tot; i ++){
        tire[i][1] = tire[i][0] = 0;
        pre[i] = 0;
    }
    tot = 1;
}
int main() {
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&k);
        init();
        for(int i = 1; i <= n; i ++){
            scanf("%d",&a[i]);
        }
        int ans = 1e9 , pos = -1;
        insert(0 , 0);
        for(int i = 1; i <= n; i ++){
            a[i]^=a[i - 1];
            int p = find(30 , 1 , a[i]);
            if(i - p < ans && p != 0){
                ans = i - p;
                pos = p + 1;
            }
            insert(i , a[i]);
        }
        if(pos == -1){
            printf ("-1\n");
        }
        else{
            printf ("%d %d\n",pos , pos + ans - 1);
        }
    }
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值