Codeforces 1863F Divide, XOR, and Conquer

rizynvu發表於2024-04-22

\(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}\)

  1. 首先若 \(s_{l, R} = 0\),那麼肯定有 \(s_{r + 1, R} = s_{l, r}\),那麼 \([l, r]\) 肯定能被遍歷到。
  2. 否則考慮令 \(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;
}

相關文章