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;
}
posted @ 2024-07-08 16:20  SmileMask  阅读(14)  评论(0编辑  收藏  举报