CodeForces - 1983F
题目大意
定义一个子数组的权值为 \(a_i \oplus a_j\),给定一个数组,求 \(kth\) 权值。
分析
可以想到二分解决,问题转化为求一个值的排名。
对于子数组,可以枚举右端点,求满足异或和 \(\le mid\) 的最大端点。
,去更新当前所有右端点的最大左端点 \(L\),每个右端点的贡献为 \(L_{now}\)。
关键问题在于如何找到最大端点,考虑用 \(01-trie\) 解决问题。
具体过程比较好想,这里直接写出:
-
若 \(a_i,mid\) 当前位都为 1。那么 1 子树都可以取,更新最大端点,然后进入 0 子树。
-
若 \(mid\) 当前位为 1。那么 0 子树都可以去,更新最大端点,然后进1子树。
-
若 \(a_i\) 当前位为 1. 进入 1 子树。
-
若 \(a_i,mid\) 当前位都为 0。 进入0子树。
代码
void insert(int val,int pos){
int u=1;
for(int i=29;~i;i--){
if(!ch[u][val>>i&1])
ch[u][val>>i&1]=++cnt;
mx[u]=max(mx[u],pos);
u=ch[u][val>>i&1];
}
mx[u]=max(mx[u],pos);
}
int query(int val,int mid){
int u=1,L=0;
for(int i=29;~i;i--){
if(!u) return L;
if((val>>i&1)&&(mid>>i&1)){
if(ch[u][1]) L=max(L,mx[ch[u][1]]);
u=ch[u][0];
}
else if(mid>>i&1){
if(ch[u][0]) L=max(L,mx[ch[u][0]]);
u=ch[u][1];
}
else if(val>>i&1) u=ch[u][1];
else u=ch[u][0];
}
if(u) L=max(L,mx[u]);
return L;
}
bool check(int mid){
int L=0;
int sum=0;
for(int i=1;i<=cnt;i++)
ch[i][0]=ch[i][1]=mx[i]=0;
cnt=1;
for(int i=1;i<=n;i++){
L=max(L,query(a[i],mid));
sum+=L;
insert(a[i],i);
}
return sum>=k;
}
void Main(){
n=rd,k=rd;
for(int i=1;i<=n;i++)
a[i]=rd;
int l=0,r=(1<<30)-1,ans=-1,mid=0;
while(l<=r){
mid=(l+r)>>1ll;
if(check(mid)) r=mid-1,ans=mid;
else l=mid+1;
}
cout<<ans<<endl;
}