给定 \(n\) 个自然数 \(a_1,\cdots,a_n\),\(m\) 次询问自然数 \(v\),求 \(\bigoplus_{i=1}^n(a_i+x)\) 的值。
\(n,m\le 2.5\cdot 10^5\),\(0\le a_i,x<2^{60}\),强制在线。
考虑暴力,设现在要求答案的第 \(j\) 位,而 \(a_i+v\) 的第 \(j\) 位即为 \(a_i\oplus v\) 的第 \(j\) 位异或上 \(a_i+v\) 的第 \(j-1\) 位是否进位,设 \(a'_{j,i}=a_i\&(2^{j+1}-1)\),后者也即 \(a'_{j-1,i}\ge 2^j-(v\&(2^j-1))\),对 \(a'_{j,i}\) 排序后二分即可,时间复杂度 \(O((n+m)\log n\log V)\)。
而排序预处理的部分可以利用上一次排序的结果:设对 \(j\) 预处理出的 \(a'_{j,i}\) 排序后的结果为 \(b_{j,1}\le\cdots\le b_{j,n}\),直接将 \(b_{j-1,i}\) 中第 \(j\) 位为 \(0,1\) 的分离即得 \(b_{j,i}\)。
考虑对二分过程也类似处理。预处理 \(s_{0/1,j,i}\) 表示 \(b_{j-1,1},\cdots,b_{j-1,i}\) 中有多少个第 \(j\) 位为 \(0/1\) 的,查询时从小到大枚举 \(j\),维护 \(x\) 表示满足上述不等式(第 \(j\) 位进位)的 \(i\) 的个数。
- 若 \(v\) 的第 \(j\) 位为 \(1\),则后 \(j\) 位进位时贡献为 \(s_{1,j,n}-s_{1,j,n-x}\),不进位时贡献为 \(s_{0,j,n-x}\),然后令 \(x:=n-s_{0,j,n-x}\)。
- 若 \(v\) 的第 \(j\) 位为 \(0\),则后 \(j\) 位进位时贡献为 \(s_{0,j,n}-s_{0,j,n-x}\),不进位时贡献为 \(s_{1,j,n-x}\),然后令 \(x:=s_{1,j,n}-s_{1,j,n-x}\)。
时间复杂度 \(O((n+m)\log V)\),空间复杂度 \(O(n\log V)\)。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long LL;
const int N = 250003;
int n, m, op, p[N], t[2][N], s[61][N];
LL a[N], v, ans;
int main(){
ios::sync_with_stdio(false);
cin >> n >> m >> op;
for(int i = 1;i <= n;++ i){cin >> a[i]; p[i] = i;}
for(int i = 0;i < 61;++ i){
t[0][0] = t[1][0] = 0;
for(int j = 1;j <= n;++ j){
bool x = a[p[j]]>>i&1;
t[x][++t[x][0]] = p[j];
s[i][j] = t[0][0];
}
for(int i = 1;i <= t[0][0];++ i) p[i] = t[0][i];
for(int i = 1;i <= t[1][0];++ i) p[i+t[0][0]] = t[1][i];
}
while(m --){
cin >> v; if(op) v ^= ans >> 20;
int x = 0; ans = 0;
for(int i = 0;i < 61;++ i)
if(v >> i & 1){
ans |= ((x ^ s[i][n]) & 1ll) << i;
x = n - s[i][n-x];
} else {
ans |= ((n ^ x ^ s[i][n]) & 1ll) << i;
x += s[i][n-x] - s[i][n];
}
printf("%llu\n", ans);
}
}