题目
题目链接
给定
2
e
5
2e5
2e5个整数,求出有多少个连续子区间
[
l
,
r
]
[l,r]
[l,r],其区间异或和的因子数量是偶数。
思路
因子一般都是成对存在的,只有完全平方数的因子数量才是奇数,除了完全平方数的其它数的因子数量都为偶数。遇难则反,取答案的补集,问题可以转化为有多少个区间的异或和为完全平方数。
因为 a i ≤ n = 2 e 5 < 2 18 − 1 a_{i}≤n=2e5<2^{18}-1 ai≤n=2e5<218−1,因此符合条件的完全平方数一定在 2 18 − 1 = 262143 2^{18}-1=262143 218−1=262143这一范围内,也可以放宽表述为在 2 n 2n 2n范围内。这样的完全平方数大约是 2 n \sqrt{2n} 2n个。
我们可以通过统计前缀异或和,通过枚举平方数寻找异或差值的方式来统计数量。
s u m sum sum数组为异或前缀和数组,先求出总数 a n s ans ans,对于不符合的情况减去。对每一个 i i i,求以 i i i为结尾的序列不满足条件的数量。
当处于 i i i时, s u m [ 0 ] sum[0] sum[0]到 s u m [ i − 1 ] sum[i-1] sum[i−1]已经记录进数组 c n t cnt cnt中,所以对所有的 j ∗ j ∈ [ 0 , 2 18 − 1 ) j*j∈[0,2^{18}-1) j∗j∈[0,218−1),如果有 k ∈ [ 0 , i − 1 ] k∈[0,i-1] k∈[0,i−1], s u m [ i ] X O R ( j ∗ j ) = s u m [ k ] sum[i] XOR(j*j)=sum[k] sum[i]XOR(j∗j)=sum[k],那么一定有 s u m [ i ] X O R s u m [ k ] = j ∗ j sum[i]XORsum[k]=j*j sum[i]XORsum[k]=j∗j,所以 a [ k + 1 ] a[k+1] a[k+1]到 a [ i ] a[i] a[i]这一连续子序列一定是不符合条件的。那么每次减去的就是 s u m [ k ] sum[k] sum[k]已经出现的次数,即 c n t [ s u m [ k ] ] cnt[sum[k]] cnt[sum[k]]。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 2e5+5;
ll a[maxn],sum[maxn],odd[maxn];
ll cnt[1LL<<18];
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
ll num = 0;
for(ll i=0;i*i<(1LL<<18);i++){
odd[++num]=i*i;
}
ll t;cin>>t;
while(t--){
ll n;cin>>n;
for(ll i=1;i<=n;i++)cin>>a[i],sum[i]=sum[i-1]^a[i];
ll ans = n*(n+1)/2;
cnt[0]=1;
for(ll i=1;i<=n;i++){
for(ll j=1;j<=num;j++){
ans-=cnt[sum[i]^odd[j]];
}
cnt[sum[i]]++;
}
for(ll i=1;i<=n;i++)cnt[sum[i]]=0;
cout<<ans<<endl;
}
return 0;
}
总结
其实比赛的时候,这道题的主要思路我基本分析清楚了,只是前缀异或和这部分实在不会优化。这题的巧妙之处在于将 O ( n 2 ) O(n^{2}) O(n2)复杂度优化为 O ( n 2 n ) O(n\sqrt{2n}) O(n2n),其思想和哈希有异曲同工之妙。