記 \(s_{l, r} = \oplus_{i = l}^r a_i\)。
考慮到這個相當於是 \([l, r]\) 內分裂區間,可以考慮區間 \(\text{DP}\)。
即記 \(f_{l, r}\) 為 \([l, r]\) 區間是否能被遍歷到。
轉移考慮對於 \([l, r]\),考慮在已知的條件下(\(len\ge r - l + 1\))\([l, r]\) 是否合法。
即到這個狀態時再從之前的狀態算過來。
考慮到因為拆分割槽間 \(l, r\) 肯定不動。
假設 \(l\) 不動,那麼就相當於是 \([l, R]\to [l, r](R > r)\),不妨就考慮由 \(s_{l, r}\) 和 \(s_{l, R}\) 來判斷 \([l, R]\) 能否遍歷到 \([l, r]\)。
那麼有 \(s_{r + 1, R} = s_{l, r}\oplus s_{l, R}\)。
- 首先若 \(s_{l, R} = 0\),那麼肯定有 \(s_{r + 1, R} = s_{l, r}\),那麼 \([l, r]\) 肯定能被遍歷到。
- 否則考慮令 \(w = \operatorname{highbit}(s_{l, R})\),那麼因為 \(> w\) 位 \(s_{l, R}\) 都為 \(0\),那麼這些位 \(s_{l, r}\) 和 \(s_{r + 1, R}\) 肯定都是相同的,然後到了第 \(w\) 位,因為在這一位 \(s_{l, R}\) 為 \(1\),那麼這一位 \(s_{l, r}\) 和 \(s_{r + 1, R}\) 肯定不同。
所以說在第 \(w\) 位若 \(s_{l, r}\) 為 \(1\) 那麼 \([l, r]\) 就能被遍歷到。
發現這些資訊都只需要在左端點和右端點的時候維護。
對於第一種情況,只需要記錄對應端點存不存在合法且異或和 \(= 0\) 的區間。
對於第二種情況,例如左端點 \(l\),記錄一個 \(g_{l, w}\) 為是否存在 \([l, r]\) 滿足 \([l, r]\) 合法且 \(\operatorname{highbit}(s_{l, r}) = w\)。
那麼判斷的時候就相當於是詢問是否存在位 \(i\) 使得 \(g_{l, i} = 1\) 且 \(s_{l, r}\) 第 \(i\) 位為 \(1\)。
那麼這個就相當於是取交集,就可以用 \(\operatorname{bitand}\) 來快速算。
時間複雜度 \(\mathcal{O}(n^2)\)。
程式碼
#include<bits/stdc++.h>
using ull = unsigned long long;
const int maxn = 1e4 + 10;
ull a[maxn];
ull fl[maxn], fr[maxn];
int fl0[maxn], fr0[maxn];
inline void Main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%llu", &a[i]), a[i] ^= a[i - 1];
for (int i = 1; i <= n; i++)
fl[i] = fr[i] = 0, fl0[i] = fr0[i] = 0;
for (int len = n; len; len--)
for (int l = 1, r = len; r <= n; l++, r++) {
ull v = a[r] ^ a[l - 1];
int f = len == n || ((v & fl[l]) > 0) || ((v & fr[r]) > 0) || fl0[l] || fr0[r];
if (l == r) printf("%d", f);
if (f) {
if (! v)
fl0[l] = fr0[r] = 1;
else
fl[l] |= 1ull << std::__lg(v), fr[r] |= 1ull << std::__lg(v);
}
}
puts("");
}
int main() {
int T;
scanf("%d", &T);
while (T--)
Main();
return 0;
}