问题描述
在一个神秘的世界中,存在着一个称为"异或森林"的地方。异或森林中的每个树木都拥有独特的力量。肖恩进入了这片森林,他得到了一个任务:找出数组中满足条件的连续子数组,使得连续子数组中所有元素异或运算结果的因数个数为偶数。完成任务将揭示宝藏的所在地。现在,你能告诉肖恩有多少个连续子数组满足条件吗?
注意:0 的因数个数视为奇数。
输入描述
第一行输入一个数字 n 表示数组元素个数。
第二行输入 n 个数字,第 i 个数字 a[i] 表示数组的第 i 个元素。
数据保证 1 ≤ n ≤ 1 0 4 , 1 ≤ a [ i ] ≤ n 1≤n≤10^4 ,1≤a[i]≤n 1≤n≤104,1≤a[i]≤n 。
输出描述
输出一个数字表示满足条件的连续子数组的数量。
样例输入
5
1 2 3 4 5
7
解题思路
什么叫做因数个数为偶数?
对于18来说,其因子有:1,2,3,6,9,18。显然是偶数个因子。一般来说因子都是成对存在的,比如1* 18=18,2* 9=18,3*6=18,但是当这个数为某个数的平方时,其中一对因子是相等的,导致因子数为奇数。
比如16的因子为:1,2,4,8,16,因子数为奇数,因为4*4=16这里4=4。
这句话翻译成子数组中所有元素异或运算结果不是平方数。
对于区间异或,比如:
(a[1]^a[2]^...^a[l]^...^a[r])^(a[1]^a[2]^...^a[l-1])=pre_xor[r]^pre_xor[l-1]=a[l]^...^a[r]
(证明查看位运算的异或运算原理)
pre_xor[r]^pre_xor[l-1]
代表前缀异或。
这里的区间异或成功转化成前缀异或。
代码
对于平方数,可以先根据数的范围
1
≤
n
≤
1
0
4
,
1
≤
a
[
i
]
≤
n
1≤n≤10^4,1≤a[i]≤n
1≤n≤104,1≤a[i]≤n 预估平方数。
这里10000对应2的14位(16384)大概最多枚举到128。
求出[l,r]区间满足异或得到平方数的数量,再被子数组数量总数减掉。
# 数组元素个数
n=int(input())
# 数组元素
a=list(map(int,input().split()))
# 预处理前缀异或
pre_xor=[0]*n
pre_xor[0]=a[0]
# pre_xor[l-1]^pre_xor[r]=x(枚举的平方数)=>pre_xor[l-1]=x^pre_xor[r]
# 求出左边有多少个pre_xor[l-1]等于x^pre_xor[r]
for i in range(1,n):
pre_xor[i]=pre_xor[i-1]^a[i]
# 用ans存储区间异或值为平方数的子区间个数
ans=0
# 从小到大枚举所有的平方数x
for i in range(129):
x=i*i
# dic存储pre_xor的每个数字出现的次数
dic={}
# 最开始pre_xor[-1]=0,即0的次数为1
dic[0]=1
# 枚举区间右端点
for r in range(n):
ans+=dic.get(pre_xor[r]^x,0)
# 更新dic
dic[pre_xor[r]]=dic.get(pre_xor[r],0)+1
ans=n*(n+1)//2-ans
print(ans)