hdu6995-Xor sum

题目链接

题意:给定一数组A,问最少连续几个数的异或和大于等于k

解法:
得到A数组的前缀异或数组X,每次询问时固定右端点j,查看j的左方有无i使得X[j]^X[i]>=k,如果有,则取最大的i,如果j-i+1>所记录的答案ans,则更新答案。用01字典树记录前缀异或数值,在01字典树的每个节点设置一个标志flag,记录可以到达这个节点的X[i]的下标i,对于每一个询问端点j,我们在01字典树中做这样的搜索:
        ①如果k二进制下某位是1,而X[j]是d,那么我们需要询问能否到达该位为d^1的节点,如果不存在,在还未访问过的数均不能能与X[j]异或后大于等于k,如果存在,则继续询问下一位
        ②如果k二进制下某位是0,而X[j]是d,如果能够到达该位置为d^1的节点,如果存在,则找到了一个异或X[j]使得大于等于k的数,记录其记录的flag,如果不存在,则继续询问下一位。
        ③当搜索完成后,如果记录有flag的话,由于要使长度最小,所以如果j-flag+1>ans,则更新ans

AcCode:

#include<iostream>
#include<algorithm>


using namespace std;

const int N = 1e5 + 10;
int xor_sum[N];
int nxt[32 * N][2], index[N * 32];

struct tree {
	int cnt = 0;
	tree() { nxt[0][0] = nxt[0][1] = 0; };
	void insert(int x, int id) {
		int pos = 0;
		for (int i = 30; i >= 0; i--) {
			int d = (x >> i) & 1;
			if (!nxt[pos][d]) {
				nxt[pos][d] = ++cnt;
				index[cnt] = -1;
				nxt[cnt][0] = nxt[cnt][1] = 0;
			}
			pos = nxt[pos][d];
			index[pos] = id;
		}
	}
	void getLeft(int x, int k, int id, int& l, int& r) {
		int pos = 0;
		int ret = -1;
		for (int i = 30; i >= 0; i--) {
			int d1 = (x >> i) & 1;
			int d2 = (k >> i) & 1;
			if (d2) {
				pos = nxt[pos][d1 ^ 1];
			}
			else {
				if (nxt[pos][d1 ^ 1]) ret = max(ret, index[nxt[pos][d1 ^ 1]]);//走这步可以大于k
				pos = nxt[pos][d1];
			}
			if (!pos) break;//没找过的数不存在使得异或x后大于等于k的数
		}
		if (pos) ret = max(ret, index[pos]);//pos存在说明走到这一步可以大于等于k
		if (ret >= 0 && (r - l + 1) > (id - ret + 1)) r = id, l = ret;

	}

};

signed main() {
	int t;
	scanf("%d", &t);
	while (t--) {
		int n;
		int k;
		scanf("%d %d", &n, &k);
		int l = -1, r = n;
		for (int i = 1; i <= n; i++) {
			scanf("%d", &xor_sum[i]);
			xor_sum[i] = xor_sum[i - 1] ^ xor_sum[i];
		}
		tree tire;
		for (int i = 0; i <= n; i++) {
			tire.getLeft(xor_sum[i], k, i, l, r);
			tire.insert(xor_sum[i], i);
		}
		if (l != -1) printf("%d %d\n", l + 1, r);
		else printf("-1\n");
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值