0
点赞
收藏
分享

微信扫一扫

2021“MINIEYE杯”中国大学生算法设计超级联赛(1)- 1006(Xor sum)

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

题目:

给定T组测试用例,对于每组测试用例有:
第一行输入两个数n, k
第二行输出n个数组成的序列[a1,a2…an]

求满足子序列的值异或和大于等于k的序列,满足条件的序列中长度最小的子序列,输出子序列的左右端点,如果存在多组满足条件的最小子序列,则输出左端点更小的

思路:

异或运算具有自反性,如果要求每个区间的异或值,我们可以利用前缀异或和的思想,即​​sum[i]​​​ 代表 ​​a[1] ^ a[2] ^ a[3] ^....^ a[i]​​​ 而​​[l, r]​​区间的异或和 = ​​sum[r] ^ sum[l - 1]​

我们可以将每一个​​sum[i]​​​从高位到低位存储到​​01trie​​​中,这样对于当前​​sum[i]​​​,前​​i - 1​​​个sum值我们已经挂到​​01trie上​​​,所以我们可以求得前​​i​​​个序列中,满足​​sum[i] ^ sum[l] >= k​​​ 的离当前​​i​​​ 最近的​​l​​​, 而为了求得最近的​​l​​​,我们每插入一个​​sum​​​值时,对于路径上的节点,都标记为当前​​i​​ 值。

而对于查找 ​​>= k​​​ 的异或值时,当异或值的最高的前 ​​j​​​ 位 ​​>​​​ ​​k​​​ 的前​​j​​​位的值时,这条路径往下不管怎么走,总的异或值肯定 ​​>​​​ k;而当异或值的最高的前 ​​j​​​ 位 ​​<​​​ ​​k​​​ 的前​​j​​​位的值时,这条路径往下不管怎么走,总的异或值肯定 ​​<​​ k.

​ACCode​

#include<iostream>

using namespace std;
typedef long long ll;

// 32进制数
const int N = 32 * (1e5 + 10);

// cur, 当前值;
// sum[]: 异或前缀和
ll cur, sum[N], k;
// tree[]: 01trie;
// cnt[]: 最右出现的位置;
// idx: 当前新建节点(新节点的索引)
int tree[N][2], cnt[N], idx, t, n;

// 插入操作
void insert(int x, int pos){

int p = 0;
for (int i = 31; i >= 0; i--) {

int t = x >> i & 1;
if(!tree[p][t]){

tree[p][t] = ++idx;
}
p = tree[p][t];
// 记录t这个节点对应最大/右的点
cnt[p] = max(cnt[p], pos);
}
}

ll find(int x){
// 当前节点
int p = 0;
// 最大异或值
ll res = 0;
// k的前i项值
ll ck = 0;
for (int i = 31; i >= 0; i--) {

int t = x >> i & 1;
int tk = k >> i & 1;

ck = ck << 1 | tk;

if(tree[p][!t]){
res = res << 1 | 1;
p = tree[p][!t];

}else{
res = res << 1;
p = tree[p][t];
}

if(res < ck){
return 0;
}

if(res > ck){
return cnt[p] + 1;
}
}

if(res >= ck) return cnt[p] + 1;

return 0;
}

int main(){

scanf("%d", &t);

while(t--){

scanf("%d%lld", &n, &k);

ll len = n + 1;
ll l = -1, r = -1;

for (int i = 1; i <= n; ++i) {
scanf("%lld", &cur);

if(len != 1 && cur >= k){
l = i;
r = i;
len = 1;
}

sum[i] = cur ^ sum[i-1];
}

insert(0, 0);

for (int i = 1; i <= n; ++i) {

if(len == 1) break;

insert(sum[i], i);

ll cl = find(sum[i]);

if(cl && (i - cl + 1) < len){
len = (i - cl + 1);
l = cl;
r = l + len - 1;
}
}

if(len != (n + 1)){
printf("%lld %lld\n", l, r);
}else{
printf("-1\n");
}

for (int i = 0; i <= idx; ++i) {
cnt[i] = 0;
tree[i][0] = 0;
tree[i][1] = 0;
}
idx = 0;
}

return 0;
}


举报

相关推荐

0 条评论