CF1592E Bored Bakry
阿新 • • 發佈:2021-11-19
題目
給定一個 \(n\) 個數的陣列,要求找一個連續子區間,滿足該子區間的區間 \(\&\) 和 \(>\) 區間異或和。求該子區間的最大長度。
思路
一開始還在想什麼資料結構,二分什麼的,然後發現\(\&\)有單調性而異或完全沒有.
然後翻題解.
題解的意思是第\(k\)位,再列舉區間,使得區間\(\&\)起來後第\(k\)位為\(1\),異或起來後第\(k\)位為\(0\),為了保證區間\(\&\)更大,我們又要強制讓區間異或起來後比\(k\)更高的位置為\(0\).
我們處理一個字首陣列\(pre_i=(a_1\oplus a_2\oplus a_3\oplus \ldots \oplus a_i)>>(k+1)\)
我們需要找的一個區間\([l,r]\),使得該區間滿足以下三個條件:
- \(r-l+1\),即區間長度為偶數,這就能保證異或後第\(k\)位為0.
- 區間內所有數第\(k\)為全為\(1\).
- \(pre_r\oplus pre_{l-1}=0\).
但是,其實要我說,第一個條件可以被第三個條件包含,只要將\(pre\)陣列改為前\(i\)個數,大於等於第\(k\)位的異或和即可.
兩個數異或和為\(0\)
程式碼
#include <iostream> #include <cstdio> #include <cstring> #include <unordered_map> using namespace std; int read() { int re = 0; char c = getchar(); while(c < '0' || c > '9')c = getchar(); while(c >= '0' && c <= '9') re = (re << 1) + (re << 3) + c - '0' , c = getchar(); return re; } const int N = 1e6 + 10; int n , k; int a[N]; int ans = 0; int pos[1 << 20];//pos[i]表示異或和為i的第一個位置為pos[i],目前沒有則為-1 int main() { n = read(); for(int i = 1 ; i <= n ; i++) a[i] = read(); memset(pos , -1 , sizeof(pos)); for(int k = 19 ; k >= 0 ; k--) { int l = 0 , r = 0; while(l <= n) { l = r + 1; while(l <= n && ((a[l] >> k) & 1) == 0)++l; r = l; while(r <= n && ((a[r] >> k) & 1) == 1)++r; --r; if(l > n)continue;//l,r:[l,r]內所有數的第k為均為1 int sum = 0;//字首 pos[sum] = l - 1; for(int i = l ; i <= r ; i++) { sum ^= (a[i] >> k); if(pos[sum] == -1) pos[sum] = i; else ans = max(ans , i - pos[sum]); } sum = 0 , pos[sum] = -1;//清空 for(int i = l ; i <= r ; i++) sum ^= (a[i] >> k) , pos[sum] = -1; } } cout << ans; return 0; }