我们可以维护异或前缀和,那么 lll 到 rrr 的异或和就为 al −1⨁ara l −1⨁a ral −1⨁ar,问题就变成了询问区间 [l−1,r][l−1,r][l−1,r] 中,有多少对 lll 和 rrr 满足 al−1 ⨁ar =k。a l−1 ⨁a r =k。 al−1 ⨁ar =k。
我们对于每个值,用莫队维护它出现的次数和在它之前满足与这个值异或起来为 kkk 的数的个数,添加或删除点时答案对应加上或减去维护的个数即可。
CODE:
说几个坑点和不好理解的点吧 :
tot[0]=1tot[0]=1tot[0]=1 : 由于询问是对于区间 [l−,r][l− ,r][l−,r] 的,所以询问范围为 [0,n][0,n][0,n],又因为 a0 =0a 0 =0a0 =0,所以要写上这句话,不然对于异或前缀和为 kkk 的位置,显然有一个合法区间 [1,x][1,x][1,x],其所对应的 lll 为 000,但由于 tot0 =0tot 0 =0tot0 =0,没有统计答案。
l=0l=0l=0: 由于询问范围为 [0,n][0,n][0,n],自然 lll 初始值为 000 (这个 shabershabershaber 因为这个调了半天)。
ql=q[i].l−1ql=q[i].l-1ql=q[i].l−1 : 由于询问是对于区间 [l−1,r][l−1,r][l−1,r]。
voidAdd(intx)sum+=tot[a[x]c];tot[a[x]]++;void Add(int x) { sum+=tot[a[x]^c]; tot[a[x]]++; }voidAdd(intx)sum+=tot[a[x]c];tot[a[x]]++; : 由于询问是对于区间 [l−1,r][l−1,r][l−1,r] 的,且 l≤rl≤rl≤r,所以在异或前缀和数组中这一定是两个位置,如果先写第二句话的话,当 k=0k=0k=0 时,sumsumsum 在统计答案时会会把当前位置算进当前位置的答案中加上,显然不合法,于是多算答案。
voidDel(intx)tot[a[x]]−−;sum−=tot[a[x]c];void Del(int x) { tot[a[x]]--; sum-=tot[a[x]^c]; }voidDel(intx)tot[a[x]]−−;sum−=tot[a[x]c];: 其实大致思路同上一条,由于询问是对于区间 [l−1,r][l−1,r][l−1,r] 的,且 l≤rl≤rl≤r,所以在异或前缀和数组中这一定是两个位置,如果先写第二句话的话,当 k=0k=0k=0 时,sumsumsum 在统计答案时会把当前位置算进当前位置的答案中减去,显然不合法,于是少算答案。
tot[200010]tot[200010]tot[200010] 由于异或前缀和可能大于 nnn,最大值为 131071131071131071,故开这么大。
l 和 r 的加减与 AddAddAdd 和 DelDelDel 的先后循序 : 删除是删除当前位置,故先 DelDelDel 再加减,添加是添加下一个位置,故先加减再 Add (这应该没人错吧 qwq )。
由于是区间数量,记得开 long long。
对于上文第五条错误 hack :
2
4 1 0
0 1 0 1
2 4
正确输出 :
2
错误输出 :
1
对于上文第八条错误 hack : (叉了6篇)
2
100000 1 0
(100000个0)
1 100000
正确输出 :
5000050000
错误输出 :
705082704