【题目链接】
【思路要点】
- 若我们将每一位分开考虑,异或可以看做模 2 2 2 意义下的加法。
- 因此,一个位置 f k , x f_{k,x} fk,x 的值可以看做从 ( k , x ) (k,x) (k,x) 出发,每次可以选择从 ( x , y ) (x,y) (x,y) 走到 ( x − 1 , y ) , ( x , y + 1 ) (x-1,y),(x,y+1) (x−1,y),(x,y+1) ,最终停在 ( 0 , x ) ( f 0 , x = 1 ) (0,x)\ (f_{0,x}=1) (0,x) (f0,x=1) 处的方案数对 2 2 2 取模。
- 因此, f k 1 , x ≡ ∑ i = 0 k 1 − k 2 f k 2 , x + i ∗ ( k 1 − k 2 i ) ( M o d 2 ) f_{k_1,x}\equiv\sum_{i=0}^{k_1-k_2}f_{k_2,x+i}*\binom{k_1-k_2}{i}\ (Mod \ 2) fk1,x≡∑i=0k1−k2fk2,x+i∗(ik1−k2) (Mod 2) 。
- 注意到由 L u c a s Lucas Lucas 定理,当且仅当 a a a 在二进制下数位包含 b b b , ( a b ) % 2 = 1 \binom{a}{b}\%2=1 (ba)%2=1 ,上式有特殊情况: f k , x ≡ f k − 2 i , x + f k − 2 i , x + 2 i ( M o d 2 ) f_{k,x}\equiv f_{k-2^i,x}+f_{k-2^i,x+2^i}\ (Mod \ 2) fk,x≡fk−2i,x+fk−2i,x+2i (Mod 2) 。
- 现在我们来考虑一个询问 ( k , x ) (k,x) (k,x) ,由上面的推断,我们令 k = k & ( 2 18 − 1 ) k=k\&(2^{18}-1) k=k&(218−1) 不会使答案发生变化,下令 k = O ( N ) k=O(N) k=O(N) 。
- 若 k ≤ O ( N ) k≤O(\sqrt{N}) k≤O(N) ,我们可以预处理出 f k , ∗ f_{k,*} fk,∗ 的结果,直接查询。
- 若 k > O ( N ) k>O(\sqrt{N}) k>O(N) ,令 b i t bit bit 表示 k k k 最高的为 1 1 1 的位置,则有 f k , x = f k − 2 b i t , x ⊕ f k − 2 b i t , x + 2 b i t f_{k,x}=f_{k-2^{bit},x}\oplus f_{k-2^{bit},x+2^{bit}} fk,x=fk−2bit,x⊕fk−2bit,x+2bit ,递归求解即可。
- 时间复杂度 O ( N N + Q N ) O(N\sqrt{N}+Q\sqrt{N}) O(NN+QN) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2e5 + 5; const int MAXM = 512; const int goal = 511; const int Min = 262143; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, type; int a[MAXM][MAXN]; int work(int pos, int k) { if (k == 0) return a[type][pos]; int tmp = k & -k, ans = 0; ans ^= work(pos, k ^ tmp); if (pos + tmp <= n) ans ^= work(pos + tmp, k ^ tmp); return ans; } int main() { read(n), read(m); for (int i = 1; i <= n; i++) read(a[0][i]); for (int i = 1; i <= goal; i++) for (int j = 1; j <= n; j++) a[i][j] = a[i - 1][j] ^ a[i - 1][j + 1]; for (int i = 1; i <= m; i++) { int k, x; read(k), read(x); k &= Min, type = k & goal; writeln(work(x, k ^ type)); } return 0; }