https://acm.hdu.edu.cn/showproblem.php?pid=6955
题意:
找最短的异或和>=k的连续子序列
先求前缀异或和a[]
问题转化成求l和r(l<r),满足a[r]^a[l]>=k且r-l最小
用字典树存储每个节点对应异或区间的最靠后的位置
把0位置异或0加进字典树
然后枚举右端点i
先查询能以i为右端点的最靠后的左端点
再把当前位置的前缀异或值加入字典树
查询方式:
从高到低枚举k的每一位,假设对应位当前前缀异或值是w(0或者1)
如果k的这一位是1,那么必须要走w^1方向。
如果k的这一位是0,那么先用w^1方向的最靠后位置更新,再走w方向。因为只要这一位的异或结果是1,就会满足>k的条件,只需要再找到这一位暂时相等,要通过后面的位决出大小的位置。
#include<bits/stdc++.h> using namespace std; #define N 100001 int tr[N*30][2],mx[N*30],tot; int a[N]; int bit[30]; void add(int val,int pos) { int w,now=0; for(int j=29;j>=0;--j) { w=(val>>j)&1; if(!tr[now][w]) tr[now][w]=++tot; now=tr[now][w]; mx[now]=pos; } } int main() { int T,n,k,al,ar,r,now,w; memset(mx,-1,sizeof(-1)); scanf("%d",&T); while(T--) { al=1; ar=1e9; scanf("%d%d",&n,&k); for(int i=0;i<30;++i) bit[i]=(k>>i)&1; for(int i=1;i<=n;++i) { scanf("%d",&a[i]); a[i]^=a[i-1]; } add(0,0); for(int i=1;i<=n;++i) { r=-1; now=0; for(int j=29;j>=0;--j) { w=(a[i]>>j)&1; if(!bit[j]) { r=max(r,mx[tr[now][w^1]]); now=tr[now][w]; } else now=tr[now][w^1]; if(!now) break; } r=max(r,mx[now]); if(r>-1 && i-r<ar-al) { al=r; ar=i; } add(a[i],i); } if(ar-al<n) printf("%d %d ",al+1,ar); else printf("-1 "); for(int i=0;i<=tot;++i) { mx[i]=-1; tr[i][0]=tr[i][1]=0; } tot=0; } }