【例题】lanqiao3400 异或森林

问题描述
在一个神秘的世界中,存在着一个称为"异或森林"的地方。异或森林中的每个树木都拥有独特的力量。肖恩进入了这片森林,他得到了一个任务:找出数组中满足条件的连续子数组,使得连续子数组中所有元素异或运算结果的因数个数为偶数。完成任务将揭示宝藏的所在地。现在,你能告诉肖恩有多少个连续子数组满足条件吗?

注意:0 的因数个数视为奇数。

输入描述
第一行输入一个数字 n 表示数组元素个数。

第二行输入 n 个数字,第 i 个数字 a[i] 表示数组的第 i 个元素。

数据保证 1 ≤ n ≤ 1 0 4 , 1 ≤ a [ i ] ≤ n 1≤n≤10^4 ,1≤a[i]≤n 1n1041a[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 1n1041a[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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值