首先我们要注意到一个关键点:x的最高位最多只会异或一次
在x往左走的过程中,如果遇到最高位不及x的,那么吃掉他不会影响最高位;遇到最高位大于x的,那么吃不掉,直接停了。遇到最高位等于x的,要么吃不掉,要么吃掉之后最高位异或变为了0,再遇到最高位等于x的就吃不掉了。
所以我们对于每次询问的处理步骤是,从当前x的位置,向左找到第一个最高位>=x的数wi,然后判断,如果吃不掉,那么能够吃掉的就是n到i+1,如果能吃掉,那么更新x的值,重复上述步骤。因为每次操作都会让x至少减少一位,所以上述步骤最多执行logx次
怎么快速找到从当前位置向左,第一个最高位>=x的数wi?我们记x的最高位是msb(x) (most significant bit) ,然后开一个数组pre,其中pre[i][j]代表从第i个位置向左,第一个最高位>=j的数的下标。我们对读入的w数组进行预处理得到pre数组,更新步骤为
if msb(𝑤𝑖)<𝑗 then pre𝑖,𝑗=pre𝑖−1,𝑗
else pre𝑖,𝑗=𝑖
时间复杂度为nlogx
```cpp
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
ll msb(ll x)
{
return __builtin_clzll(0ll)- __builtin_clzll(x);
}
int main(){
int T;
cin>>T;
while(T--){
int n,q;
cin>>n>>q;
vector<ll> w(n+3,0),backsum(n+3,0);
vector<vector<ll>> pre(n+3,vector<ll>(100,0));
for(int i=1;i<=n;++i){
cin>>w[i];
}
for(int i=n;i>=1;i--){
backsum[i]=backsum[i+1] ^ w[i];//后缀和数组,便于快速更新x的值
}
for(int i=1;i<=n;++i)
{
for(int j=1;j<=64;++j)
{
if(msb(w[i])<j)
pre[i][j]=pre[i-1][j];
else
pre[i][j]=i;
}
}
while(q--){
ll x;
cin>>x;
ll cur=n+1;//当前已经吃掉了第几个
ll temp=x;
while(temp>0&&cur>1)
{
ll i=pre[cur-1][msb(temp)];
if(i==0)
{
cur=1;
break;
}
temp=x ^ backsum[i+1];
if(w[i]>temp)
{
cur=i+1;
break;
}
else
{
temp=x ^ backsum[i];
cur=i;
}
}
cout<<n+1-cur<<" ";
}
cout<<endl;
}
}
```