首先确定肯定使用字典树
然后因为异或的性质有
那么只需要把所有的前缀插入字典树
然后枚举端点去字典树上找有多少个即可
也就是对每一个都去字典树上找有多少个前缀满足条件
那么最后答案需要除以,因为每个一
可以作为字典树上的前缀,也可以作为
值得一提的是需要把0插入进去
因为允许存在
那么我们也对前缀去字典树上查询,因为最后的答案是除以二的
如何查询???
因为是二进制数比较大小,所以当的这一位是
时
异或值必须是,否则无解,继续往下查询
当的这一位不是
时
异或值是,那么直接累加符合条件的前缀个数
然后异或值是,继续查询
最后返回符合条件的个数即可
时间复杂度
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e7+10;
const int N = 1e6+10;
int id,n,k,num[maxn],zi[maxn][2],a[N];
ll ans;
void insert(int x)
{
int now = 0;
for(int i=30;i>=0;i--)
{
int u = (x>>i)&1;
if( zi[now][u]==0 ) zi[now][u] = ++id;
now = zi[now][u];
num[now]++;//标记这个节点下有多少儿子
}
}
int ask(int x)
{
int now = 0,ans = 0;
for(int i=30;i>=0;i--)
{
int u = (x>>i)&1;
if( zi[now][u^1] )//这一位可以为1
{
if( (k>>i)&1 ) now = zi[now][u^1];//k这一位也有1,必须为1
else//k这一位没有
{
ans += num[zi[now][u^1]];
if( zi[now][u] ) now = zi[now][u];
else return ans;
}
}
else//只能为0
{
if( (k>>i)&1 ) return ans;
else now = zi[now][u];
}
}
ans += num[now];
return ans;
}
int main()
{
scanf("%d%d",&n,&k);
int now = 0; insert( now );
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
now ^= a[i]; insert( now );
}
now = 0;
for(int i=1;i<=n;i++)
now ^= a[i],ans += ask( now );
ans += ask(0);
cout << ans/2;
} ```

京公网安备 11010502036488号