子数组异或和(前缀和、哈希)

题意

给定一个长度为\(n\)的整数数组\(a_1, a_2, \dots, a_n\)

请你统计一共有多少个数组\(a\)的非空连续子数组能够同时满足以下所有条件:

  • 该连续子数组的长度为偶数。
  • 该连续子数组的前一半元素的异或和等于其后一半元素的异或和。

题目链接:https://www.acwing.com/problem/content/4510/

数据范围

\(2 \leq n \leq 3 \times 10^5\)
\(0 \leq a_i < 2^{20}\)

思路

首先分析一下性质。

如果前一半元素的异或和等于其后一半元素的异或和,那么说明这个区间所有元素异或和为\(0\)。并且如果某个区间所有元素的异或和为\(0\),无论怎么划分,左边元素的异或和一定等于右边元素的异或和。

区间异或和可以通过异或版的前缀和解决。如果两个点的前缀和相同,那么区间异或为\(0\)

如果没有长度为偶数的限制,我们可以直接维护一个哈希表。现在加上偶数的限制,那么我们可以同时维护一个长度为奇数的哈希表,和一个长度为偶数的哈希表。因为两端点的下标都是奇数或者都是偶数时,区间长度才为偶数,因此对两个哈希表分别计算即可。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>

using namespace std;

typedef long long ll;

const int N = 300010, M = 2000010;

int n, a[N], cnt[M][2];

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) {
        scanf("%d", &a[i]);
        a[i] ^= a[i - 1];
    }
    ll ans = 0;
    for(int i = 1; i <= n; i ++) {
        if(!a[i] && i % 2 == 0) ans ++;
        ans += (ll)cnt[a[i]][i % 2];
        cnt[a[i]][i % 2] ++;
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2022-08-16 11:32  pbc的成长之路  阅读(168)  评论(0编辑  收藏  举报