UOJ681【UR #22】月球列車【二進位制,Trie】
阿新 • • 發佈:2021-12-16
給定 \(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))\)
而排序預處理的部分可以利用上一次排序的結果:設對 \(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\)
- 若 \(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)\)
#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);
}
}