异或和不小于k的最短连续子序列(01字典树)
题目:
Description
Given a sequence of integers of length nn, find the shortest consecutive subsequence witch XOR sum not less than kk.
If there are multiple consecutive subsequences of the same length, print the consecutive subsequence with the smallest left end point.
If there are no consecutive subsequence witch XOR sum not less than k, just print "-1".
Input
The first line contains a single integer t (t≤100) representing the number of test cases in the input. Then t test cases follow.
The first line of each test case contains two integers n (1≤n≤100000) and k (0≤k<2^30), representing the length of sequence.
The second line of each test contains nn integers ai (0≤ai<2^30), representing the integers in sequence.
The number of test witch n>1000 does not exceed 5.
Output
For each test case, print two integers in one line, representing the left end point and right end point of the consecutive subsequence.
If there are no consecutive subsequence witch XOR sum not less than k, print "-1" in one line.
Samples
Source
2021杭电-1
题解:
首先做这个题的时候以要知道一些异或前缀和的知识,就是说:sum[i]为a[i]的异或前缀和,那么a[l]^a[l+1]^a[l+2]^---a[r]。
然后区间异或处理:处理整个数组的前缀异或和(每个前缀和的值与整个字典树查找最大值与K的关系) 操作完成之后再将这个值插入字典树
————原理(假设当前枚举到的前缀和在数组中的位置为R 众所周知a[r] ^ a[l] = a[l —> r -1]的异或和 )
由X xor X = 0 ; 0 xor Y = Y;所有【l,r】 = 【1,r】 ^ 【1,l - 1】
这样在一颗加入了r 前的所有前缀异或和的01字典树上查找【1,r】就能得到以r为右边界的最大异或和
插入的时候要注意在节点上做一个标记cnt[i]为最大的下标
然后查询的时候很类似这个题可以看一下:传送门
就是这样的
x | 0 | 0 | 1 | 1 |
如果想让x大于k的话,这一位是1的话就更新答案,0就接着走 | 如果想让x大于k的话,这一位必须为1 | 如果想让x大于k的话,如果为0就更新答案,1就接着走 | 如果想让x大于k的话,这一位必须位0 | |
k | 0 | 1 | 0 | 1 |
代码:
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn=5e6+100; int tree[maxn][2]; int a[maxn]; int cnt[maxn]; int idx=0; int n,k; void insert(int x,int s){ int root=0; for(int i=31;i>=0;i--){ int op=(x>>i)&1; if(!tree[root][op]){ tree[root][op]=++idx; } root=tree[root][op]; cnt[root]=max(cnt[root],s); } } void inint(){ for(int i=0;i<=idx;i++){ cnt[i]=tree[i][0]=tree[i][1]=0; } } int get(int x){ int root=0; int l=0; for(int i=31;i>=0;i--){ int opx=(x>>i)&1; int opk=(k>>i)&1; if(!opx){ if(!opk){ l=max(l,cnt[tree[root][1]]); if(!tree[root][0]) return l; root=tree[root][0]; } else{ if(!tree[root][1]) return l; root=tree[root][1]; } } else{ if(!opk){ l=max(l,cnt[tree[root][0]]); if(!tree[root][1]) return l; root=tree[root][1]; } else{ if(!tree[root][0]) return l; root=tree[root][0]; } } } return l = max(l,cnt[root]); } int main(){ int t; cin>>t; while(t--){ scanf("%d%d",&n,&k); inint(); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); a[i]^=a[i-1]; } if(n==1||k==0){ cout<<1<<" "<<1<<endl; continue; } idx=0; insert(0,0); int ans=0x3f3f3f3f; int ansl=0,ansr=0; for(int i=1;i<=n;i++){ int l=get(a[i]); if(l&&i-l<ans){ ans = i-l; ansl = l + 1; ansr = i; } insert(a[i],i); } if(ans==0x3f3f3f3f){ cout<<-1<<"\n"; } else{ cout<<ansl<<" "<<ansr<<"\n"; } } return 0; }