题目链接
https://codeforces.com/contest/617/problem/E
题目大意
给你一个长度为 N 的序列和 M 个查询,每个查询问你区间 [L , R] 中有多少子区间异或和为 K
解题思路
莫队 + 前缀异或和
设 sum[i] 为序列的前 i 个数的异或和,那么根据异或性质可知一段区间 X , Y的异或和就为 sum[y] ^ sum[x - 1]
于是可以将询问转换为区间 [L , R] 中有多少对 X , Y 使得 sum[y] ^ sum[x - 1] = k
设 cnt[i] 表示异或和 i 出现的次数,那么常规的做法就是对于每次询问从 L 遍历到 R 一边更新答案一遍记录前缀异或和
map<int , int>cnt; for(int i = 1 ; i <= m ; i ++) { int ans = 0 ; cnt.clear(); cnt[0] = 1; for(int j = q[i].l; j <= q[i].r ; j ++) { int now = sum[q[i].l - 1]; //把 [1 - (L - 1)] 的异或和去掉 ans += cnt[sum[j] ^ k ^ now]; cnt[sum[j] ^ now] ++ ; } cout << ans << ' '; }
显然这么做复杂度是肯定不行的,而题目又不涉及修改区间等操作,所以可以套个莫队离线操作
AC_Coder
#include<bits/stdc++.h> #define ios std::ios::sync_with_stdio(false) #define rep(i,a,n) for (int i=a;i<=n;i++) #define int long long using namespace std; const int N = 2e5 + 10; struct Q{ int l , r , id; }q[N]; int a[N] , pos[N] , sum[N] , ans[N] ; unordered_map<int , int>cnt; int n , m , k , sz , l = 1 , r , res; bool cmp(Q a , Q b) { if(pos[a.l] == pos[b.l]) return a.r < b.r; return pos[a.l] < pos[b.l]; } void Add(int x) { res += cnt[sum[x] ^ k]; cnt[sum[x]] ++ ; } void Sub(int x) { cnt[sum[x]] -- ; res -= cnt[sum[x] ^ k]; } signed main() { ios; cin >> n >> m >> k; sz = sqrt(n); rep(i , 1 , n) cin >> a[i] , sum[i] = sum[i - 1] ^ a[i] , pos[i] = i / sz; rep(i , 1 , m) { cin >> q[i].l >> q[i].r; q[i].id = i; } cnt[0] = 1; sort(q + 1 , q + 1 + m , cmp); rep(i , 1 , m) { while(q[i].l < l) -- l , Add(l - 1); while(q[i].l > l) Sub(l - 1) , l ++; while(q[i].r > r) Add(++ r); while(q[i].r < r) Sub(r --); ans[q[i].id] = res; } rep(i , 1 , m) cout << ans[i] << ' '; return 0; }