2 条题解

  • 1
    @ 2026-3-14 15:59:49

    思路分析

    首先考虑一个朴素做法:

    • 枚举所有区间 [l,r][l,r]
    • 计算区间异或和

    时间复杂度是 O(n2)O(n^2),当 n=105n=10^5 时显然会超时(约 101010^{10} 次操作)。

    前缀异或优化

    我们定义前缀异或和数组:

    b[i]=a[1]a[2]a[i]b[i] = a[1] \oplus a[2] \oplus \cdots \oplus a[i]

    那么区间 [l,r][l,r] 的异或和为:

    b[r]b[l1]b[r] \oplus b[l-1]

    转化问题

    要求:

    b[r]b[l1]=xb[r] \oplus b[l-1] = x

    等价于:

    b[l1]=b[r]xb[l-1] = b[r] \oplus x

    核心思想

    在遍历到 rr 时:

    • 只需要统计之前有多少个满足 b[l1]=b[r]xb[l-1] = b[r] \oplus x
    • 用一个哈希表记录前缀异或和出现次数

    特殊情况处理

    当:

    b[r]x=0b[r] \oplus x = 0

    即:

    b[r]=xb[r] = x

    说明区间 [1,r][1,r] 本身就是一个合法区间。

    为了统一处理这种情况,需要:

    cnt[0] = 1;
    

    表示“空前缀”出现过一次。

    算法流程

    1. 初始化:

      • cnt[0] = 1
      • ans = 0
    2. 遍历数组:

      • 计算前缀异或和 b[i]
      • 查询 cnt[b[i] ^ x]
      • 累加到答案
      • 更新 cnt[b[i]]

    代码实现(C++)

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 1e5 + 10;
    
    int a[N], b[N];
    
    int main() {
        int n, x;
        cin >> n >> x;
    
        unordered_map<int, int> cnt;
        cnt[0] = 1;
    
        ll ans = 0;
    
        for(int i = 1; i <= n; i++) {
            cin >> a[i];
            b[i] = b[i - 1] ^ a[i];
    
            if(cnt.count(b[i] ^ x)) {
                ans += cnt[b[i] ^ x];
            }
    
            cnt[b[i]]++;
        }
    
        cout << ans;
        return 0;
    }
    

    信息

    ID
    456
    时间
    1000ms
    内存
    256MiB
    难度
    7
    标签
    递交数
    133
    已通过
    35
    上传者