E. Beautiful Subarrays
思路
显然有ai⨁ai+1⨁……⨁an=(a1⨁a2⨁……⨁an)⨁(a1⨁a2⨁……⨁ai−1)a_i\bigoplus a_{i + 1} \bigoplus ……\bigoplus a_{n} = (a_1 \bigoplus a_2 \bigoplus……\bigoplus a_{n}) \bigoplus (a_1 \bigoplus a_2 \bigoplus …… \bigoplus a_{i - 1})ai⨁ai+1⨁……⨁an=(a1⨁a2⨁……⨁an)⨁(a1⨁a2⨁……⨁ai−1)
所以我们可以先求一遍异或前缀和,然后再通过枚举右端点,去查找有多少个左端点符合要求。
假设我们当前枚举到iii这个位置,前iii个数的异或前缀和是aaa,我们要查找异或值大于等于kkk的,显然我们可以去找一个xxx,满足a⨁k=xa \bigoplus k = xa⨁k=x,这个时候大于等于xxx的数就是满足要求的数了,这个操作我们也可以通过一颗01trie01trie01trie树来进行查找。
分四种情况:
- a 的当前位二进制数是0,k的当前位二进制数也是0,如果我们选择一个数的当前位是1,显然与a异或之后会变大,所以对答案有贡献,所以我们加上答案,然后到当前位上是0的地方去继续寻找。
- a的当前位二进制数是0, k的当前位二进制数是1,显然我们要使查找值不变小,一定要到当前位是1上去找,这个时候对答案没有哦贡献。
- a的当前位二进制数是1, k的当前位二进制数是0,如果我们选择1,异或结果变大,对答案有贡献,所以加上答案,走到当前位是1的地方去继续寻找答案。
- a的当前位二进制数是1, k的当前位二进制也是1,这个时候要保证异或结果不变小,只能到当前位是0的地方去寻找。
最后再特判一下刚好相等的情况就OK了。
代码
/*Author : lifehappy
*/
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>#define mp make_pair
#define pb push_back
#define endl '\n'
#define mid (l + r >> 1)
#define lson rt << 1, l, mid
#define rson rt << 1 | 1, mid + 1, r
#define ls rt << 1
#define rs rt << 1 | 1using namespace std;typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;const double pi = acos(-1.0);
const double eps = 1e-7;
const int inf = 0x3f3f3f3f;inline ll read() {ll f = 1, x = 0;char c = getchar();while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}while(c >= '0' && c <= '9') {x = (x << 1) + (x << 3) + (c ^ 48);c = getchar();}return f * x;
}const int N = 4e7 + 10;int trie[N][2], tot, num[N];void insert(int x) {int rt = 0;for(int i = 30; i >= 0; i--) {int now = x >> i & 1;if(!trie[rt][now]) trie[rt][now] = ++tot;rt = trie[rt][now];num[rt]++;}
}int find(int a, int b) {int ans = 0, rt = 0;for(int i = 30; i >= 0; i--) {int u = a >> i & 1, v = b >> i & 1;if(!u) {if(v) {if(!trie[rt][1]) return ans;rt = trie[rt][1];}else {ans += num[trie[rt][1]];if(!trie[rt][0]) return ans;rt = trie[rt][0];}}else {if(!v) {ans += num[trie[rt][0]];if(!trie[rt][1]) return ans;rt = trie[rt][1];}else {if(!trie[rt][0]) return ans;rt = trie[rt][0];}}}return ans + num[rt];
}const int N1 = 1e6 + 10;int a[N1], n, k;int main() {// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);ll ans = 0;n = read(), k = read();for(int i = 1; i <= n; i++) {a[i] = read();a[i] ^= a[i - 1];}insert(0);//插入0,有可能这个数自己就比k大。for(int i = 1; i <= n; i++) {ans += find(a[i], k);insert(a[i]);}printf("%lld\n", ans);return 0;
}