题目
思路
妹妹说:“其实不怎么难,真的是道签到题。” 替代理论冒头了,母系社会正在崛起!
首先拆位。记 v w v_w vw 为 a i a_i ai 的按位取反值的前 w w w 位(即删去 2 w 2^w 2w 及高位),同理可记 x w x_w xw,显然 ( a i + x ) (a_i{+}x) (ai+x) 在 2 w 2^w 2w 这一位上,在 x w + 1 ∈ ( v w , v w + 2 w ] x_{w+1}\in(v_w,\;v_w{+}2^w] xw+1∈(vw,vw+2w] 时是 0 / 1 0/1 0/1(会进位),其余时候则是 1 / 0 1/0 1/0(不进位)。所以答案就是在 a n s ∣ x = 0 = ( ⨁ a i ) ans\Big|_{x=0}=(\bigoplus a_i) ans∣ ∣x=0=(⨁ai) 的基础上,再对每个 w w w 询问有多少个 v w v_w vw 或 ( v w + 2 w ) (v_w{+}2^w) (vw+2w) 小于 x w + 1 x_{w+1} xw+1,若为奇数个,则需异或 2 w 2^w 2w 。
直接取出分界点 v w v_w vw 和 ( v w + 2 w ) (v_w{+}2^w) (vw+2w),询问时二分查找,复杂度 O ( n log a log n ) \mathcal O(n\log a\log n) O(nlogalogn),直接 T \textit T T 飞。
怎样优化?发现分界点 v v v 是若干后缀。我就开始考虑从低位向高位插入的 t r i e \tt trie trie 了,然后人傻了。我甚至猜测 ⌊ lastans 2 20 ⌋ \lfloor{\text{lastans}\over 2^{20}}\rfloor ⌊220lastans⌋ 有无深意。
应注意到,比较 x w + 1 x_{w+1} xw+1 和 v w v_w vw 的大小时,要么比较最高位,要么比较后面的位。而后面的位恰好是 v w − 1 v_{w-1} vw−1,已经比较过。这是类似 基数排序 的思想,即先比较较低位,确定(只看较低位时的)相对排名,然后再将较高位纳入考虑。
那么至少所有分界点是可以 O ( n log a ) \mathcal O(n\log a) O(nloga) 排序的——本质上就是对 a i a_i ai 的按位取反结果做低位到高位的基数排序,过程中得到的排名。
查询的时候,相当于是已知低位的 rank \text{rank} rank,所以预处理 f w ( i , 0 / 1 ) f_w(i,0/1) fw(i,0/1) 表示 x w x_{w} xw 在 { v w } \{v_w\} {vw} 中的 rank \text{rank} rank 是 i i i 时, x w + 1 = x w + ( 0 / 1 ) × 2 w x_{w+1}=x_w+(0/1)\times 2^w xw+1=xw+(0/1)×2w 在 { v w + 1 } \{v_{w+1}\} {vw+1} 中的 rank \text{rank} rank 。这可以在基排的同时轻松求出。
时间复杂度
O
(
n
log
a
)
\mathcal O(n\log a)
O(nloga),真的是道让我成为脑瘫的题呢。我直接 Shaya 化。
代码
统计答案时,由于实际是求 x w + 1 x_{w+1} xw+1 在 { v w , v w + 2 w } \{v_w,\;v_w{+}2^w\} {vw,vw+2w} 中的排名,浅分类讨论即可。
#include <bits/stdc++.h>
// OUYE the God is watching us.
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
int a = 0, c = getchar(), f = 1;
for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
for(; isdigit(c); c=getchar()) a = a*10+(c^48);
return a*f;
}
inline llong readlong(){
llong a = 0; int c = getchar();
for(; !isdigit(c); c=getchar());
for(; isdigit(c); c=getchar()) a = a*10+(c^48);
return a; // no negative support
}
inline void writelong(const llong &x){
if(x > 9) writelong(x/10);
putchar(int(x%10)^48);
}
const int LOGA = 61, MAXN = 250000;
llong a[MAXN+1], b[MAXN+1];
int dp[LOGA][MAXN+1][2];
int main(){
int n = readint(), q = readint(), opt = readint();
llong base = 0;
rep(i,1,n){
a[i] = readlong();
base ^= a[i], a[i] = ~a[i];
}
for(int w=0; w!=LOGA; ++w){
int rnk = dp[w][0][0] = 0;
rep(i,1,n) rnk += !int(a[i]>>w&1), dp[w][i][0] = rnk;
int ptr[2] = {0,rnk}; dp[w][0][1] = rnk;
rep(i,1,n) rnk += int(a[i]>>w&1), dp[w][i][1] = rnk;
rep(i,1,n) b[++ ptr[a[i]>>w&1]] = a[i];
memcpy(a+1,b+1,n<<3);
}
for(llong x,lst=0; q; --q){
x = readlong(); if(opt) x ^= (lst>>20);
lst = base; int rnk = 0;
rep0(i,0,LOGA){
if(x>>i&1){
if((rnk^n)&1) // x-2^w > v and x > v
lst ^= (1ll<<i);
rnk = dp[i][rnk][1];
}
else{
if(rnk&1) lst ^= (1ll<<i);
rnk = dp[i][rnk][0];
}
}
writelong(lst), putchar('\n');
}
return 0;
}
在这里悄悄向校长检讨自己的过错 =(