题目链接:第 k 大区间
因为k很大,显然可以想到二分。利用前缀异或和变成两个点的异或。然后就是枚举每一个后缀,求异或大于某个数的个数。
直接Trie上查询即可。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int n,k,a[N],ch[N*32][2],sz[N*32],idx;
inline void insert(int x){
int p=0;
for(int i=30;i>=0;i--){
int k=x>>i&1;
if(!ch[p][k]) ch[p][k]=++idx;
p=ch[p][k]; sz[p]++;
}
}
inline int ask(int x,int mid){
int p=0,s=0,cnt=0;
for(int i=30;i>=0;i--){
int k=x>>i&1;
if(s+(1<<i)>=mid) cnt+=sz[ch[p][k^1]],p=ch[p][k];
else s+=(1<<i),p=ch[p][k^1];
if(!p) return cnt;
}
return cnt;
}
inline int check(int mid){
int sum=0,s=0;
memset(ch,0,sizeof ch),memset(sz,0,sizeof sz),idx=0;
insert(0);
for(int i=1;i<=n;i++){
s^=a[i],sum+=ask(s,mid);
insert(s);
}
return sum>=k;
}
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
int l=0,r=4e9;
while(l<r){
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l;
return 0;
}