解题思路
首先我们要知道异或运算的两个特性:
如果有两个相同的数进行异或运算,那么运算的结果为?0?,比如?2⊕2=0。
一个数和0?进行异或运算,该数不变,比如2⊕0=2?。
题目让我们求得是区间?[l,r]?中出现次数为奇数的数的异或和,那么我们就要去掉那些出现次数为偶数的数再进行异或运算。
但实际上,因为那些数的出现次数是偶数,所以我们对它们进行异或和运算后,结果为0。所以这些出现次数为偶数的数,即便我们再进行异或和运算的时候把它们也进行了运算,也不会影响我们的结果的。
换句话来说,就是我们直接计算第?l?个数到第?r?个数的异或和就行。不需要处理那些出现次数为偶数的数。
现在问题就是如何快速的求出第?l?个数到第?r?个数的异或和。
这里还是要用到上面的第一个性质,比如我们要计算?33?到?55?的异或和:
一段?11?到?55?的异或和运算为:1⊕2⊕3⊕4⊕5。
一段?11?到?22?的异或和运算为:?1⊕2。
两段进行异或和运算,我们可以知道,11?和?22?都进行了两次异或,所以它们就不会影响结果了,此时,我们就得到了3⊕4⊕5?,即?3?到?5?的异或和。
大家可以发现,这和我们的前缀和思想非常相似,我们可以称之为前缀异或。
用?11?个数组?ss?来预处理前缀异或:s[i]?表示第?11?个数到第?i个数的前缀异或和。
对于每次询问[l,r]?,我们只需计算s[r]⊕s[l?1]?即可。
一共有m?次询问,处理每次询问的复杂度为?O(1)?。
注意,因为本题输入输出数据较大,所以要用快一些的输入输出方式,比如?scanf?和?printf?。如果想使用cin?和cout?,需要关闭流同步。
以及换行不能使用?endl?作为换行符,要用??n?。
时间复杂度:O(m)?。
AC_Code
C++
#includeiostream
usingnamespacestd;
#includevector
#includealgorithm
constintN=1e5+50;
inta[N],s[N];
intmain()
{
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
intn,m,l,r;
cinnm;
for(inti=1;i=n;i++)
{
cina[i];
s[i]=s[i-1]^a[i];
}
while(m--)
{
cinlr;
cout(s[r]^s[l-1])endl;
}
return0;
}