#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; } const int N = 1e5+10, M = 1e5+10, Z = 1e9 + 7, ms63 = 0x3f3f3f3f; int n, m, k; struct A { int id; int o; int l, r; bool operator < (const A&b)const { if (id != b.id)return id < b.id; return r < b.r; } }a[M]; LL ans[M]; int num[(int)1.05e6]; int c[N]; int ins(int p) { int tmp = num[c[p] ^ k]; ++num[c[p]]; return tmp; } int del(int p) { --num[c[p]]; int tmp = num[c[p] ^ k]; return tmp; } int main() { while (~scanf("%d%d%d", &n,&m,&k)) { int len = sqrt(n); for (int i = 1; i <= n; ++i) { scanf("%d", &c[i]); c[i] ^= c[i - 1]; } for (int i = 1; i <= m; ++i) { scanf("%d%d", &a[i].l, &a[i].r); --a[i].l; a[i].id = a[i].l / len; a[i].o = i; } sort(a + 1, a + m + 1); MS(num, 0); LL ANS = 0; int l = a[1].l; int r = a[1].l-1; for (int i = 1; i <= m; ++i) { while (l > a[i].l)ANS += ins(--l); while (r < a[i].r)ANS += ins(++r); while (l < a[i].l)ANS -= del(l++); while (r > a[i].r)ANS -= del(r--); ans[a[i].o] = ANS; } for (int i = 1; i <= m; ++i)printf("%lld\n", ans[i]); } return 0; } /* 【trick&&吐槽】 做题的心态千万要稳住。 1,检查是否爆int————不要只检验答案输出是lld还是d,所有定义也都要检查下 2,检查数组是否开小。这道题虽然数字的范围都是1e6,然而,异或之后还是可达2^20-1的。 于是,数组还是要开到1<<20的 3,这题的异或是对区间 【题意】 给你n(1e5)个数,每个数的权值都在[0,1e6]之间。 有m(1e5)个询问, 对于每个询问,给定区间[l,r],问你区间内有多少个连续子段(按照题目定义,显然非空), 满足子段的连续异或和恰好为k(k是[0,1e6]范围的数) 【类型】 莫队算法 真是区间思想 【分析】 1,这题询问众多,而且询问都是[l,r]的形式。 我们显然想到使用莫队做。 2,涉及到区间连续异或和,我们想到用前缀和映射思想。 3,数字范围最大只有1e6,于是可以开差不多(其实要1<<20大小),做映射。 这题最关键的地方,在于,比如l=5,r=6,我们实际是可以需要用c[6]^c[4]来得到val[5]+val[6] 这里浪费了很多时间,并且想了很多复杂的方法。 然而,我们如果一旦发现到其真实区间,问题就变得轻而易举了! 我们发现,对于我们选定的一个区间段,比如l=5,r=5,其实是有两个位置的,5号位置前,5号位置后,位置的选择方式并不是r-l-1种,而是r-l种 于是,我们对于所有的查询区间[l,r],使得l--,使其变成其真实区间[l-1,r]。 然后,我们把所有前缀和x都用num[]数组计数,就可以更新答案了。 如果是区间扩展操作,在这个前缀和添加进去之间的num[x^k]就是对答案的增贡献,然后+=num[x] 如果是区间缩减操作,先-=num[x],然后在这个前缀和删除之后的num[x^k]就是对答案的减贡献。 为什么区间扩展操作先+=num[x^k]再把这个值加进去 而如果是区间缩减操作,则是把这个值删除,再-=num[x^k] 为什么这样子呢?为了防止x^k==x,即这个区间段和自身做匹配的情况发生。 自身与自身做匹配,说明选择的是空区间段。显然是不行的。 于是,一个左区间段-1对应到真实区间,一个加减顺序解决掉k==0的特殊情况, 用在一起这道题就可以AC啦。 【时间复杂度&&优化】 O(msqrt(n)) 【数据】 5 4 0 0 0 0 0 0 1 5 3 3 5 5 2 4 */