【UR #22】月球列车

【UR #22】月球列车

下文中,\(x\) 的第 \(i\) 位是指 \(x\) 对应二进制中 \(2^i\) 的系数。

同理,\(x\) 的前 \(i\) 位是指 \(x\) 对应二进制中第 \(0,1,\dots,i\) 位合在一起的结果。

考虑求出 \(a_i + x\) 的第 \(j\) 位,其为以下三个值的异或:

  • \(a_i\) 的第 \(j\) 位

  • \(x\) 的第 \(j\) 位

  • \(a_i\) 的前 \(j-1\) 位与 \(x\) 的前 \(j-1\) 位相加是否进位。

前两部分可以在最后算答案的时候异或上去,主要问题是第三部分。

我们令 \(b_{j,i} = a_i \land (2^j - 1)\),即 \(a_i\) 的前 \(j-1\) 位。

后者要求 \(b_{j,i} + x \land (2^j - 1) \geq 2^j\)。

我们稍作转化,即 \(b_{j,i} \geq 2^j - x \land (2^j - 1)\)。

考虑对 \(b_j\) 排序,此时我们在 \(b_j\) 中二分 \(2^j - x \land (2^j - 1)\) 即可。

此时我们得到了 \(O(n \log n \log V)\) 的做法,考虑优化。

首先是预处理 \(b_j\) 部分,我们只需要获得 \(b_j\) 排序后的结果。

\(b_j\) 是在 \(b_{j-1}\) 的基础上添加最高位得到的,考虑将 \(0\) 移到 \(1\) 的前面。

只要不改变相对顺序,这个算法就是正确的,可以做到 \(O(n \log n)\) 预处理。

接下来考虑查询部分,我们仍然考虑观察 \(2^j - x \land (2^j - 1)\) 的性质。

我们举几个不同的例子。

\(x=101001\) 对应 \(000001,000001,000011,000111,000111,010111\)。

\(x=101100\) 对应 \(000001,000010,000100,000100,000100,010100\)。

也就是说,在 \(\text{lowbit}\) 之前,询问的数一直是 \(2^j\),可以预处理。

事实上我们并不需要预处理,因为此时 \(a_j\) 的前 \(j-1\) 位加上 \(0\) 不可能进位。

接下来在 \(\text{lowbit}\) 之后,每次询问的数是在前一个数上加一位形成的。

这个过程可以 \(O(1)\) 转移,于是我们得到了 \(O(n \log V)\) 的复杂度。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int C=61;
int n,m,t;
long long a[250010],b[C][250010],to_pos_0[C][250010],to_pos_1[C][250010],xor_sum;
void init(){
	for(int i=1;i<=n;i++){
		xor_sum^=a[i];
	}
	for(int i=1;i<=n;i++){
		b[0][i]=i;
	}
	for(int j=1;j<C;j++){
		for(int i=1;i<=n;i++){
			if(a[b[j-1][i]]&(1ll<<(j-1))){
				to_pos_0[j-1][0]++;
			}
		}
		for(int i=1;i<=n;i++){
			if(!(a[b[j-1][i]]&(1ll<<(j-1)))){
				to_pos_0[j-1][i]=to_pos_0[j-1][i-1]+1;
				to_pos_1[j-1][i]=to_pos_1[j-1][i-1];
				b[j][to_pos_0[j-1][i]]=b[j-1][i];
			}
			else{
				to_pos_0[j-1][i]=to_pos_0[j-1][i-1];
				to_pos_1[j-1][i]=to_pos_1[j-1][i-1]+1;
				b[j][to_pos_1[j-1][i]]=b[j-1][i];
			}
		}
	}
}
long long calc(long long num){
	long long ans=xor_sum;
	if(n&1){
		ans^=num;
	}
	int pre_pos=n;
	long long lowbit=num&-num;
	num^=lowbit;
	for(int i=0;i<C;i++){
		if((1ll<<i)>lowbit){
			if(!(num&(1ll<<(i-1)))){
				pre_pos=to_pos_1[i-1][pre_pos];
			}
			else{
				pre_pos=to_pos_0[i-1][pre_pos];
			}
			if(pre_pos&1){
				ans^=1ll<<i;
			}
		}
	}
	return ans;
} 
int main(){
	scanf("%d %d %d",&n,&m,&t);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	init();
	long long last_ans=0;
	while(m--){
		long long v;
		scanf("%lld",&v);
		if(t){
			v^=last_ans>>20;
		}
		last_ans=calc(v);
		printf("%lld\n",last_ans);
	}
	return 0;
}
posted @ 2026-03-25 16:46  Oken喵~  阅读(27)  评论(0)    收藏  举报