C. Even Subarrays 题解【Codeforces Round #841 (Div. 2)】

题目

题目链接
给定 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 ain=2e5<2181,因此符合条件的完全平方数一定在 2 18 − 1 = 262143 2^{18}-1=262143 2181=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[i1]已经记录进数组 c n t cnt cnt中,所以对所有的 j ∗ j ∈ [ 0 , 2 18 − 1 ) j*j∈[0,2^{18}-1) jj[0,2181),如果有 k ∈ [ 0 , i − 1 ] k∈[0,i-1] k[0,i1] s u m [ i ] X O R ( j ∗ j ) = s u m [ k ] sum[i] XOR(j*j)=sum[k] sum[i]XOR(jj)=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]=jj,所以 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 ),其思想和哈希有异曲同工之妙。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

keguaiguai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值