异或
-
k k k 很小考虑直接枚举。可以先预处理出来符合条件的 f f f 。之后可以暴力判断每一个 f i f_{i} fi 是不是符合要求。
-
针对于一个 f i f_{i} fi 怎么快速找到一个区间呢?可以枚举区间的右端点 r r r ,于此同时使用一个前缀异或值 p r e pre pre ,设 f i f_{i} fi 为区间 [ l , r ] [l, r] [l,r] 的异或值,那么 p r e ⊕ f i pre \oplus f_{i} pre⊕fi 这个值肯定是 1 ∼ l 1\sim l 1∼l 的前缀异或值。 因为序列 a a a 是一个排列,每一个前缀都是独一无二的,因此在有 f i f_{i} fi 为区间 [ l , r ] [l, r] [l,r] 的异或值这个假设下,肯定有 f i ⊕ p r e f_{i} \oplus pre fi⊕pre 是一个前缀异或值,因此只需要开一个数组记录一下前缀异或值就好了,枚举区间右端点,之后暴力判断当前右端点是不是对答案有贡献就可。
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
void solve() {
int n;
cin >> n;
vector<int> a(n + 10, 0);
for (int i = 1; i <= n; i ++ ) {
cin >> a[i];
}
int k;
vector<long long> f(40, 0);
cin >> f[1] >> f[2] >> k;
for (int i = 3; i <= k; i ++ ) {
f[i] = f[i - 1] + f[i - 2];
if (f[i] > 20000000) {
k = i - 1;
break;
}
}
int res = 0;
vector<bool> ans(k + 10, false);
vector<bool> is_de(2 * n + 10, false);
is_de[0] = true;
for (int i = 1; i <= n; i ++ ) {
res = res ^ a[i];
for (int j = 1; j <= k; j ++ ) {
if ((res ^ f[j]) > 2 * n) continue;
if (is_de[res ^ f[j]]) ans[j] = true;
}
is_de[res] = true;
}
for (int i = 1; i <= k; i ++ ) {
if (!ans[i]) {
cout << "NO" << endl;
return;
}
}
cout << "YES" << endl;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}