CF617E XOR and Favorite Number
BZOJ 5301 [CQOI2018]异或序列 双倍经验。
一个区间的异或值可以用前缀和来表示,$a_l \bigoplus a_{l + 1}\bigoplus a_{l + 2} \bigoplus ... \bigoplus a_r = xorSum_r \bigoplus xorSum_{l - 1}$,这样子处理一下异或前缀和用莫队移动一下左右端点就可以了。
要注意的一点就是左端点$l$在移动的时候其实不是在移动自己,每一次加入删除都是$l - 1$,因为不能取两个一样的下标异或起来算答案,所以在加入删除的时候要注意判断一下先后顺序。
时间复杂度$O(n\sqrt{n})$。
Code:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; const int N = 1e5 + 5; const int M = 20; int n, qn, K, blockSiz, a[N], sum[N]; ll nowAns = 0, cnt[1 << M]; struct Querys { int l, r, id; ll res; friend bool operator < (const Querys &x, const Querys &y) { if(x.l / blockSiz == y.l / blockSiz) return x.r < y.r; else return x.l < y.l; } } q[N]; inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline void del(int now) { --cnt[sum[now]]; nowAns -= cnt[sum[now] ^ K]; } inline void add(int now) { nowAns += cnt[sum[now] ^ K]; ++cnt[sum[now]]; } inline void solve() { blockSiz = sqrt(n); sort(q + 1, q + 1 + qn); /* for(int i = 1; i <= qn; i++) printf("%d %d\n", q[i].l, q[i].r); */ cnt[0] = 1; for(int ln = 1, rn = 0, i = 1; i <= qn; i++) { for(; ln < q[i].l; del(ln - 1), ++ln); for(; ln > q[i].l; --ln, add(ln - 1)); for(; rn < q[i].r; add(++rn)); for(; rn > q[i].r; del(rn--)); q[q[i].id].res = nowAns; } } int main() { read(n), read(qn), read(K); for(int i = 1; i <= n; i++) { read(a[i]); sum[i] = sum[i - 1] ^ a[i]; } /* for(int i = 1; i <= n + 1; i++) printf("%d ", sum[i]); printf("\n"); */ for(int i = 1; i <= qn; i++) { read(q[i].l), read(q[i].r); q[i].id = i; } solve(); for(int i = 1; i <= qn; i++) printf("%lld\n", q[i].res); return 0; }